
Stitch::React Native
- 3.8k installs
- 7.8k repo stars
- Updated July 21, 2026
- google-labs-code/stitch-skills
stitch::react-native is a Google Labs skill that converts Stitch HTML designs into typed React Native components with StyleSheet, theme extraction, and MCP-driven asset fetch.
About
Stitch React Native transforms Stitch web HTML designs into production-ready React Native code using MCP tools, reliable GCS downloads, and strict HTML-to-native mapping rules. Retrieval starts with list_tools to discover the Stitch MCP prefix, get_screen for design JSON, and optional reuse of local .stitch/designs HTML and PNG files unless the user requests a refresh. High-reliability fetch-stitch.sh downloads HTML and width-parameterized screenshots because internal AI fetch fails on Google Cloud Storage redirects. HTML elements map to View, Text, Image, Pressable, TextInput, ScrollView, FlatList, SectionList, and react-native-svg primitives with SafeAreaView wrappers for notch safety. CSS and Tailwind classes convert to StyleSheet.create with column-default flexbox, numeric dimensions, platform-specific shadow via Platform.select, and theme.ts color tokens extracted from the HTML head Tailwind config. Architectural rules enforce atomic design folders, custom hooks for logic, mockData.ts for static content, readonly TypeScript Props interfaces, React Navigation screen typing, and accessibilityLabel on every interactive element. Execution validates with npm run validate plus arch.
- Stitch MCP get_screen plus fetch-stitch.sh for reliable HTML and screenshot downloads.
- HTML-to-React-Native mapping table covers View, Text, Pressable, FlatList, and SVG.
- StyleSheet rules convert Tailwind tokens to theme.ts with Platform.select shadows.
- Atomic design structure with hooks, mockData, readonly Props, and accessibility labels.
- Validation via npm run validate and architecture-checklist before simulator start.
Stitch::React Native by the numbers
- 3,758 all-time installs (skills.sh)
- +358 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #38 of 1,048 Mobile Development skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
stitch::react-native capabilities & compatibility
- Capabilities
- stitch mcp screen retrieval and reliable gcs ass · html element to react native primitive mapping w · tailwind to stylesheet conversion with platform · atomic design folder structure with typed readon · component validation and react navigation multi
- Works with
- gcp
- Use cases
- frontend · ui design
What stitch::react-native says it does
Internal AI fetch tools can fail on Google Cloud Storage domains
npx skills add https://github.com/google-labs-code/stitch-skills --skill stitchreact-nativeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3.8k |
|---|---|
| repo stars | ★ 7.8k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 21, 2026 |
| Repository | google-labs-code/stitch-skills ↗ |
How do I turn a Stitch web design into clean React Native screens with correct flexbox styles, navigation, and accessible components?
Convert Stitch HTML designs to React Native components with StyleSheet mapping, atomic design folders, theme extraction, and MCP-driven design fetch.
Who is it for?
Mobile developers using Stitch MCP who need HTML-to-React-Native conversion with atomic design structure and theme tokens.
Skip if: Skip for pure web React projects, backend APIs, or designs without Stitch MCP access and fetch scripts.
When should I use this skill?
User converts Stitch HTML to React Native, maps Tailwind to StyleSheet, or fetches Stitch screen designs via MCP.
What you get
Atomic React Native components with theme.ts, mockData.ts, validated TypeScript props, navigation wiring, and simulator-ready Metro or Expo project.
- src/theme.ts color tokens
- atomic React Native components
- NavigationContainer screen wiring
By the numbers
- Allows 5 tool classes: stitch namespace, Bash, Read, Write, web_fetch
Files
Stitch to React Native Components
You are a mobile engineer focused on transforming Stitch web designs into clean, production-ready React Native code. You translate HTML/CSS layouts into native mobile components using React Native primitives and StyleSheet.
Retrieval and networking
1. Namespace discovery: Run list_tools to find the Stitch MCP prefix. Use this prefix (e.g., stitch:) for all subsequent calls. 2. Metadata fetch: Call [prefix]:get_screen to retrieve the design JSON. 3. Check for existing designs: Before downloading, check if .stitch/designs/{page}.html and .stitch/designs/{page}.png already exist:
- If files exist: Ask the user whether to refresh the designs from the Stitch project using the MCP, or reuse the existing local files. Only re-download if the user confirms.
- If files do not exist: Proceed to step 4.
4. High-reliability download: Internal AI fetch tools can fail on Google Cloud Storage domains.
- HTML:
bash scripts/fetch-stitch.sh "[htmlCode.downloadUrl]" ".stitch/designs/{page}.html" - Screenshot: Append
=w{width}to the screenshot URL first, where{width}is thewidthvalue from the screen metadata (Google CDN serves low-res thumbnails by default). Then run:bash scripts/fetch-stitch.sh "[screenshot.downloadUrl]=w{width}" ".stitch/designs/{page}.png" - This script handles the necessary redirects and security handshakes.
5. Visual audit: Review the downloaded screenshot (.stitch/designs/{page}.png) to confirm design intent and layout details.
HTML to React Native mapping
Element mapping
Map HTML elements to React Native components using these rules:
| HTML | React Native | Notes |
|---|---|---|
<div> | View | Default container |
<span>, <p>, <h1>-<h6> | Text | All text must be wrapped in Text. Nest Text for inline styling. |
<img> | Image | Use source={{ uri }} for remote images, require() for local assets. |
<button>, <a> | Pressable | Prefer Pressable over TouchableOpacity. Use onPress instead of onClick. |
<input> | TextInput | Map placeholder, value, onChangeText. |
<scroll container> | ScrollView | For short lists only. Use FlatList for long or dynamic lists. |
<ul>/<ol> with many items | FlatList | Requires data, renderItem, keyExtractor. |
<section> with grouped data | SectionList | For grouped data with headers. Use tab navigator for tab-based layouts. |
<select> | Third-party picker or custom modal | React Native has no built-in select. |
<svg> | react-native-svg | Convert SVG markup to Svg, Path, Circle, etc. |
| Root wrapper | SafeAreaView | Wrap top-level screens to avoid notch/status bar overlap. |
Style mapping
CSS and Tailwind classes do not work in React Native. Convert all styles to StyleSheet.create():
Layout: Flexbox is the default layout system. flexDirection defaults to 'column' (not 'row' like web CSS).
display: flexis implicit on everyView.justify-contentmaps tojustifyContent.align-itemsmaps toalignItems.gapmaps togap(React Native 0.71+). For older versions, usemarginBottomon children.
Dimensions: Use numbers (not strings). width: 100 means 100 density-independent pixels.
- Percentage strings are supported:
width: '100%'. - For responsive sizing, use
useWindowDimensions()fromreact-native. - There is no
vw/vh. Calculate fromDimensions.get('window').
Typography: All text styles must be on Text components, never on View.
font-sizemaps tofontSize(number, not string).font-weightmaps tofontWeight(string:'400','700','bold').line-heightmaps tolineHeight(number).letter-spacingmaps toletterSpacing.text-transformmaps totextTransform.colorapplies toTextonly.
Borders and shadows:
border-radiusmaps toborderRadius.box-shadowdoes not exist. Useelevation(Android) andshadowColor/shadowOffset/shadowOpacity/shadowRadius(iOS).- Use
Platform.select()to apply platform-specific shadow styles.
Unsupported CSS properties (handle manually or skip):
hover,transition,animation(usereact-native-reanimatedfor animations).position: fixed(useposition: 'absolute'with manual offset).overflow: auto(useScrollView).z-indexworks but only between sibling views.opacityworks.visibility: hiddendoes not exist; use conditional rendering oropacity: 0.
Color extraction: Extract the Tailwind config from the HTML <head>. Map color tokens to a theme.ts constants file instead of hardcoding hex values in StyleSheet.
Platform-specific code
When the design requires different behavior on iOS and Android:
import { Platform } from 'react-native';
const styles = StyleSheet.create({
shadow: Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
},
android: {
elevation: 4,
},
}),
});Architectural rules
- Atomic Design: Organize components as atoms (buttons, labels, icons), molecules (input groups, cards), and organisms (headers, lists, forms). Place them in
src/components/atoms/,src/components/molecules/,src/components/organisms/. - Logic isolation: Move event handlers, API calls, and business logic into custom hooks in
src/hooks/. Components should only handle rendering. - Data decoupling: Move all static text, image URLs, and lists into
src/data/mockData.ts. Components receive data through props. - Type safety: Every component must export a TypeScript interface named
[ComponentName]Propswithreadonlyproperty modifiers. - No hardcoded styles: Extract colors, spacing, and font sizes into
src/theme.ts. Reference them inStyleSheet.create(). - Navigation: Use React Navigation for screen transitions. Define screen types with
NativeStackScreenPropsorBottomTabScreenProps. - Accessibility: Every interactive element must have
accessibilityLabelandaccessibilityRole. Images needaccessibilityLabel. UseaccessibilityStatefor toggles and checkboxes. - Safe areas: Wrap top-level screen components with
SafeAreaViewfromreact-native-safe-area-context(not the one fromreact-native). - Project specific: Focus on the target project's needs and constraints. Leave Google license headers out of the generated React Native components.
Execution steps
1. Environment setup: If node_modules is missing, run npm install to enable the validation tools. 2. Theme layer: Create src/theme.ts from the extracted Tailwind config. Define colors, spacing, typography, and shadow presets. 3. Data layer: Create src/data/mockData.ts based on the design content. Extract all text, image URIs, and list data. 4. Component drafting: Use resources/component-template.tsx as a base. Find and replace all instances of StitchComponent with the actual component name. Map HTML elements to React Native primitives following the mapping table above. 5. Navigation wiring: If the design has multiple screens, set up a NavigationContainer with a stack or tab navigator in App.tsx. 6. Quality check:
- Run
npm run validate <file_path>for each component. - Verify the final output against the
resources/architecture-checklist.md. - Start Metro with
npx react-native startornpx expo startto verify the live result on a simulator/device.
Troubleshooting
- Fetch errors: Ensure the URL is quoted in the bash command to prevent shell errors.
- Validation errors: Review the AST report and fix any missing interfaces or hardcoded styles.
- Text outside Text component: React Native crashes if raw strings appear outside
<Text>. Verify all text nodes are wrapped. - Image sizing: Unlike web
<img>, React NativeImagehas no intrinsic size. Always specifywidthandheightin styles or useaspectRatio. - FlatList vs ScrollView: If you see a "VirtualizedList inside ScrollView" warning, replace the outer
ScrollViewwith a plainViewor useFlatListListHeaderComponent/ListFooterComponent.
/**
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import { View, Text, Image, Pressable, StyleSheet, Platform } from 'react-native';
import { colors, shadows } from '../src/theme';
/**
* Gold Standard: ActivityCard
* This file is the definitive reference for the agent.
*/
export interface ActivityCardProps {
readonly id: string;
readonly username: string;
readonly action: 'MERGED' | 'COMMIT';
readonly timestamp: string;
readonly avatarUrl: string;
readonly repoName: string;
readonly onPress?: () => void;
}
export const ActivityCard: React.FC<ActivityCardProps> = ({
username,
action,
timestamp,
avatarUrl,
repoName,
onPress,
}) => {
const isMerged = action === 'MERGED';
return (
<Pressable
style={({ pressed }) => [styles.container, pressed && styles.pressed]}
onPress={onPress}
accessibilityRole="button"
accessibilityLabel={`${username} ${action.toLowerCase()} in ${repoName}`}
>
<View style={styles.leftContent}>
<Image
source={{ uri: avatarUrl }}
style={styles.avatar}
accessibilityLabel={`Avatar for ${username}`}
/>
<View style={styles.textContent}>
<Text style={styles.username} numberOfLines={1}>
{username}
</Text>
<View
style={[
styles.badge,
isMerged ? styles.badgeMerged : styles.badgeCommit,
]}
>
<Text
style={[
styles.badgeText,
isMerged ? styles.badgeTextMerged : styles.badgeTextCommit,
]}
>
{action}
</Text>
</View>
<Text style={styles.separator}>in</Text>
<Text style={styles.repoName} numberOfLines={1}>
{repoName}
</Text>
</View>
</View>
<Text style={styles.timestamp}>{timestamp}</Text>
</Pressable>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
gap: 16,
borderRadius: 8,
padding: 16,
minHeight: 56,
...shadows.card,
},
pressed: {
opacity: 0.7,
},
leftContent: {
flexDirection: 'row',
alignItems: 'center',
gap: 16,
flex: 1,
overflow: 'hidden',
},
avatar: {
width: 40,
height: 40,
borderRadius: 20,
},
textContent: {
flexDirection: 'row',
flexWrap: 'wrap',
alignItems: 'center',
columnGap: 8,
rowGap: 4,
flex: 1,
},
username: {
fontWeight: '600',
fontSize: 14,
},
badge: {
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 12,
},
badgeMerged: {
backgroundColor: colors.badgeMergedBg,
},
badgeCommit: {
backgroundColor: colors.badgeCommitBg,
},
badgeText: {
fontSize: 12,
fontWeight: '600',
},
badgeTextMerged: {
color: colors.badgeMergedText,
},
badgeTextCommit: {
color: colors.badgeCommitText,
},
separator: {
fontSize: 14,
opacity: 0.6,
},
repoName: {
fontSize: 14,
},
timestamp: {
fontSize: 14,
fontWeight: '400',
opacity: 0.5,
flexShrink: 0,
},
});
export default ActivityCard;
{
"name": "react-native-components",
"version": "1.0.0",
"description": "Design-to-code prompt to React Native components for Stitch MCP",
"type": "module",
"scripts": {
"validate": "node scripts/validate.js",
"fetch": "bash scripts/fetch-stitch.sh"
},
"dependencies": {
"@swc/core": "^1.3.100"
},
"engines": {
"node": ">=18.0.0"
}
}
Stitch to React Native Components Skill
Install
npx skills add google-labs-code/stitch-skills --skill react-native --globalExample Prompt
Convert my Login screen in my Stitch Project to React Native components.Skill Structure
This skill follows the Agent Skills open standard. Each skill is self-contained with its own logic, validation scripts, and design tokens.
skills/react-native/
├── SKILL.md — Core instructions & workflow
├── package.json — Validator dependencies
├── scripts/ — Networking & AST validation
├── resources/ — Architecture checklist & component templates
└── examples/ — Gold-standard code samplesHow it Works
When activated, the agent follows a design-to-native pipeline:
1. Retrieval: Uses a system-level curl script to download Stitch HTML and screenshots from Google Cloud Storage. 2. Mapping: Translates HTML elements to React Native primitives (View, Text, Pressable, Image, etc.) and converts CSS/Tailwind to StyleSheet.create() calls. 3. Generation: Scaffolds components using Atomic Design (atoms, molecules, organisms). 4. Validation: Runs an automated AST check to catch missing Props interfaces or hardcoded style values. 5. Audit: Performs a final self-correction check against the architecture checklist.
Architecture Quality Gate
Structural integrity
- [ ] Components organized by Atomic Design: atoms, molecules, organisms in
src/components/. - [ ] Logic extracted to custom hooks in
src/hooks/. - [ ] No monolithic files. Each component in its own file.
- [ ] All static text, image URIs, and list data moved to
src/data/mockData.ts.
Type safety and syntax
- [ ] Every component exports a
[ComponentName]Propsinterface withreadonlyproperty modifiers. - [ ] File is syntactically valid TypeScript (no parse errors).
- [ ] Placeholders from template (e.g.,
StitchComponent) have been replaced with actual names. - [ ] Navigation screen params are typed with
NativeStackScreenPropsor equivalent.
React Native primitives
- [ ] No HTML elements (
div,span,p,img,button). Only React Native components. - [ ] All text wrapped in
Textcomponents. No raw strings insideView. - [ ]
Pressableused for interactive elements (notTouchableOpacityorTouchableHighlight). - [ ]
FlatListused for dynamic/long lists (notScrollViewwith.map()). - [ ]
Imagecomponents have explicitwidthandheightoraspectRatio.
Styling
- [ ] All styles defined via
StyleSheet.create()at the bottom of the file. - [ ] No inline style objects (use
StyleSheetreferences). - [ ] No hardcoded hex values. Colors referenced from
src/theme.ts. - [ ] Shadows use
Platform.select()for iOS/Android differences. - [ ]
flexDirectionexplicitly set where row layout is needed (default iscolumn).
Accessibility
- [ ] Interactive elements have
accessibilityLabelandaccessibilityRole. - [ ] Images have descriptive
accessibilityLabel. - [ ] Toggle/checkbox elements use
accessibilityState.
Platform handling
- [ ] Top-level screens wrapped with
SafeAreaViewfromreact-native-safe-area-context. - [ ] Platform-specific code uses
Platform.select()orPlatform.OSchecks. - [ ] Responsive dimensions use
useWindowDimensions()(not hardcoded pixel values for screen-relative sizing).
import React from 'react';
import { View, StyleSheet } from 'react-native';
export interface StitchComponentProps {
readonly children?: React.ReactNode;
readonly testID?: string;
}
export const StitchComponent: React.FC<StitchComponentProps> = ({
children,
testID,
}) => {
return (
<View style={styles.container} testID={testID} accessibilityRole="none">
{children}
</View>
);
};
const styles = StyleSheet.create({
container: {},
});
export default StitchComponent;
#!/bin/bash
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
URL=$1
OUTPUT=$2
if [ -z "$URL" ] || [ -z "$OUTPUT" ]; then
echo "Usage: $0 <url> <output_path>"
exit 1
fi
mkdir -p "$(dirname "$OUTPUT")"
echo "Initiating high-reliability fetch for Stitch HTML..."
curl -L -f -sS --connect-timeout 10 --compressed "$URL" -o "$OUTPUT"
if [ $? -eq 0 ]; then
echo "Successfully retrieved HTML at: $OUTPUT"
exit 0
else
echo "Error: Failed to retrieve content. Check TLS/SNI or URL expiration."
exit 1
fi
/**
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import swc from '@swc/core';
import fs from 'node:fs';
import path from 'node:path';
const HEX_COLOR_REGEX = /#[0-9A-Fa-f]{3,8}\b/;
const RGBA_COLOR_REGEX = /^rgba?\(\s*\d/;
const HTML_ELEMENTS = ['div', 'span', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'img', 'button', 'a', 'input', 'ul', 'ol', 'li', 'section', 'header', 'footer', 'nav', 'main'];
async function validateComponent(filePath) {
const code = fs.readFileSync(filePath, 'utf-8');
const filename = path.basename(filePath);
try {
const ast = await swc.parse(code, { syntax: "typescript", tsx: true });
let hasInterface = false;
let hasExportedInterface = false;
let colorIssues = [];
let htmlElements = [];
console.log("Scanning AST...");
const walk = (node, parent) => {
if (!node) return;
if (node.type === 'TsInterfaceDeclaration' && node.id.value.endsWith('Props')) {
hasInterface = true;
if (parent?.type === 'ExportDeclaration') {
hasExportedInterface = true;
}
}
// Check for hardcoded hex values in strings
if (node.type === 'StringLiteral' && HEX_COLOR_REGEX.test(node.value)) {
colorIssues.push(node.value);
}
// Check for rgba() color strings
if (node.type === 'StringLiteral' && RGBA_COLOR_REGEX.test(node.value)) {
colorIssues.push(node.value);
}
// Check for HTML elements used as JSX tags
if (node.type === 'JSXOpeningElement' && node.name?.type === 'Identifier') {
const tagName = node.name.value;
if (HTML_ELEMENTS.includes(tagName)) {
htmlElements.push(tagName);
}
}
for (const key in node) {
if (node[key] && typeof node[key] === 'object') walk(node[key], node);
}
};
walk(ast, null);
console.log(`--- Validation for: ${filename} ---`);
let valid = true;
if (hasExportedInterface) {
console.log("PASS: Exported Props interface found.");
} else if (hasInterface) {
console.error("WARN: Props interface found but not exported. Add 'export' keyword.");
valid = false;
} else {
console.error("FAIL: Missing Props interface (must end in 'Props' and be exported).");
valid = false;
}
if (colorIssues.length === 0) {
console.log("PASS: No hardcoded color values found.");
} else {
console.error(`FAIL: Found ${colorIssues.length} hardcoded colors. Use theme.ts instead.`);
colorIssues.forEach(c => console.error(` - ${c}`));
valid = false;
}
if (htmlElements.length === 0) {
console.log("PASS: No HTML elements found. Using React Native primitives.");
} else {
const unique = [...new Set(htmlElements)];
console.error(`FAIL: Found HTML elements: ${unique.join(', ')}. Replace with React Native components.`);
valid = false;
}
if (valid) {
console.log("\nCOMPONENT VALID.");
process.exit(0);
} else {
console.error("\nVALIDATION FAILED.");
process.exit(1);
}
} catch (err) {
console.error("PARSE ERROR:", err.message);
process.exit(1);
}
}
validateComponent(process.argv[2]);
Related skills
How it compares
Pick stitch::react-native over generic HTML-to-React skills when the source designs live in Google Stitch and the target is native mobile StyleSheet output.
FAQ
Why use fetch-stitch.sh instead of web_fetch?
Internal AI fetch tools fail on Google Cloud Storage domains; the bash script handles redirects and security handshakes.
How are CSS styles converted?
Tailwind and CSS map to StyleSheet.create with column-default flexbox, numeric sizes, and theme.ts color constants from the HTML head config.
What validation runs before launch?
npm run validate per component plus architecture-checklist.md review before starting Metro or Expo.
Is Stitch::React Native safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.