Creating a Block: Step-by-Step Guide
This guide walks you through authoring and submitting a production-ready component block to RNBlocks.
Step 1: Check for Duplicates
Before writing code:
- Search
registry/blocks/to verify that a similar component does not already exist. - If a similar block exists, consider proposing an enhancement or variant rather than building a duplicate.
- For large components or multi-screen flows, open a Block Proposal first.
Step 2: Create the Directory Structure
Create a directory under registry/blocks/ named with your component's kebab-case slug:
mkdir -p registry/blocks/my-component/files
Your component directory will contain two items:
registry/blocks/my-component/
├── registry.json
└── files/
└── my-component.tsx
Step 3: Write the Component
Key Requirements
- Self-Contained: Place all component code, subcomponents, and types inside
files/my-component.tsx. Do not import from parent repository paths or relative packages. - Standard React Native Primitives: Use
StyleSheet.createfor universal compatibility across Expo and bare React Native. - Semantic Colors: Define color constants at the top of the file so users can easily customize the palette:
const COLORS_DARK = {
background: "#18181b",
card: "#27272a",
border: "#3f3f46",
text: "#fafafa",
subtext: "#a1a1aa",
accent: "#6366f1",
};
const COLORS_LIGHT = {
background: "#f4f4f5",
card: "#ffffff",
border: "#e4e4e7",
text: "#09090b",
subtext: "#71717a",
accent: "#4f46e5",
};
- Expose a Theme Prop: If your component supports theming, provide a
theme?: "dark" | "light"prop:
export interface MyComponentProps {
theme?: "dark" | "light";
// other props...
}
export default function MyComponent({ theme = "dark", ...props }: MyComponentProps) {
const colors = theme === "light" ? COLORS_LIGHT : COLORS_DARK;
// ...
}
Container-Relative Layout:
- Use
width: "100%",flex: 1, and flexbox layouts. - Never assume fixed device widths (e.g.
width: 390). - Test that the component looks natural in compact (320pt) containers as well as full-width tablets.
- Use
Accessibility & Focus:
- Add
accessibilityRoleon pressable elements (e.g."button","tab"). - Add clear
accessibilityLabelattributes to icons and buttons. - Use
accessibilityStatefor dynamic state (e.g.{ selected: isSelected, disabled: isDisabled }). - Ensure interactive touch targets meet the recommended 44x44pt minimum.
- Provide visible focus styling for keyboard and web users (e.g. outline or high-contrast focus state).
- For chart components, provide an accessible container summary rather than forcing screen-reader users through dozens of granular swipes.
- Add
Header JSDoc with Copy-Paste Usage Example: Include a concise
@exampleblock at the top offiles/my-component.tsxshowing how consumers can copy and immediately render your component:
/**
* @example
* import MyComponent from "./components/my-component";
*
* export default function Screen() {
* return (
* <MyComponent
* theme="dark"
* onAction={() => console.log("Pressed")}
* />
* );
* }
*/
Step 4: Document Props & Quick Usage
Every block must be easy for consumers to drop into their projects without guessing prop names or parsing 500 lines of implementation code.
Self-Documented Props Interface:
export interface MyComponentProps { /** Color theme variant. Defaults to "dark". */ theme?: "dark" | "light"; /** Primary accent color used for highlights and active states. */ accentColor?: string; /** Callback fired when an item is selected. */ onSelect?: (id: string) => void; }Sensible Zero-Prop Default: Whenever possible, allow
<MyComponent />to render with default mock data so developers can preview it immediately after runningnpx @rnblocks/cli add <slug>.
Step 5: Write registry.json
Create registry/blocks/my-component/registry.json:
{
"name": "my-component",
"title": "My Interactive Component",
"description": "Clear description of component functionality, interaction, and visual traits.",
"type": "block",
"author": {
"name": "Your Name",
"github": "your-github-username"
},
"version": "1.0.0",
"category": "navigation",
"tags": ["navigation", "animated", "menu"],
"files": [
{
"path": "files/my-component.tsx",
"type": "registry:component"
}
],
"dependencies": ["lucide-react-native"],
"devDependencies": {},
"registryDependencies": [],
"platforms": ["ios", "android", "web"],
"frameworks": ["expo", "react-native"],
"styling": ["StyleSheet"],
"themes": ["dark", "light"]
}
Step 6: Validate and Generate
Run the automated validation script:
pnpm run validate:registry
If validation passes, compile the registry and generate web artifacts:
pnpm run generate:registry
Step 7: Test Locally in Web Preview
Start the local development server:
pnpm dev
Visit http://localhost:3000/blocks in your browser. Verify:
- Your component card appears in the gallery with proper metadata.
- Interactive live preview renders cleanly in both dark and light modes.
- Source code view displays the exact component source.
- Quick usage snippet is clearly presented and copyable.
Step 8: Submit Your Pull Request
- Commit your changes following Conventional Commits (
feat: add my-component block). - Push your branch and open a Pull Request.
- Fill out the Pull Request template completely, including the quality checklist (roles, labels, states, touch targets, focus indicators, usage examples) and preview screenshot.
- Confirm the licensing agreement attestation in the PR description.