with one click
build-react-apps
// Produce high-quality React apps from natural language descriptions.
// Produce high-quality React apps from natural language descriptions.
Learn how to make widgets on canvas
Learn how to design and write custom agent templates (sub-agents) inside your workspace, so you can delegate specialized work to custom sub-agents.
Uplevel your researching abilities and learn how to research properly.
How authenticated HTTP calls to the Opal backend work — the host/guest architecture, fetchWithCreds, the fetch allowlist, and the OpalBackendClient migration. Read this before adding, modifying, or debugging any backend call.
Learn how to make widgets on canvas
Learn how to present your work as a structured surface — a curated manifest of artifacts that the user's application renders in real time.
| name | build-react-apps |
| title | How to build React apps |
| description | Produce high-quality React apps from natural language descriptions. |
| allowed-tools | ["files.*","sandbox.*"] |
A multi-file React component bundle rendered in a sandboxed iframe. The bundle consists of:
App.jsx — the root component that accepts configuration propscomponents/*.jsx — reusable sub-componentsstyles.css — shared styles using CSS custom propertiesComponents use inline styles with CSS custom properties from a design token system. Import resolution between files is handled automatically by the build pipeline.
Your UI MUST work from 320px to 1200px+. This is not optional.
Write the app an its components as files in your directory.
Build the bundle with bundler.mjs:
node ./skills/{name of this skill}/scripts/bundler.mjs
If you do not run this exact command, the user will see a blank screen.
Follow these rules strictly.
App.jsx and
contain a function called App.App
with realistic defaults.components/ should
render standalone with sensible defaults. Document all props with @prop
JSDoc.import React from "react" in
every JSX file. Import sub-components with relative paths (e.g.
import Header from "./components/Header").import "./styles.css" in App.jsx for shared
styles.export default its component
function.--cg- design
tokens. No hex colors, no rgb(), no named colors, no raw pixel values.
Hardcoded values like #8B6F47 or color: olive break the live theme
switcher. This is a build error, not a suggestion.localStorage, sessionStorage,
IndexedDB, or any Web Storage APIs. The iframe environment may not have
storage access due to origin restrictions. All state lives in React component
state or is passed via props and the SDK.var(--cg-color-surface*) tokens and all text must use
var(--cg-color-on-surface*) tokens. The tokens will natively map to their
theme variants.flex-wrap: wrap on any flex container with multiple children that
should stack on narrow screens.auto-fit / minmax for multi-column layouts:
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)).clamp() for font sizes that should scale with viewport:
font-size: clamp(1rem, 2vw + 0.5rem, 2rem).These particular paterns are forbidden.
width: 800px, width: 600px, or similar. Use max-width with a percentage
or min() instead: max-width: min(100%, 800px).min-width exceeding 320px on any layout container. This prevents
rendering on small screens.CRITICAL FORBIDDEN PATTERN: NO TAILWIND!
flex, min-h-screen,
p-4, bg-red-500). Tailwind is NOT INSTALLED.className="hero-container").styles.css file using Vanilla CSS.import "./styles.css"; at the top of your App.jsx. If you
don't write and import a CSS file, your design will be completely unstyled.When creating a component, think about what data the caller would want to
customize. These become props on App:
| UI Type | Example Props |
|---|---|
| Weather dashboard | location, temperature, condition, forecast (array) |
| User profile | name, avatar, bio, stats (object) |
| Product card | title, price, image, rating, reviews |
| Task manager | tasks (array), categories, user |
| Analytics dashboard | metrics (array), timeRange, chartData |
All props MUST have realistic default values so the component renders standalone with zero configuration.
Reminder: this is a hard rule (see above). Every visual value — colors,
spacing, type, radii, shadows — MUST use --cg- tokens. No exceptions.
| Category | Use | Never |
|---|---|---|
| Colors | var(--cg-color-...) | #hex, rgb(), named colors |
| Spacing | var(--cg-sp-...) | Raw pixel values for padding/margin/gap |
| Font sizes | var(--cg-text-...-size) | 14px, 1rem |
| Border radius | var(--cg-radius-...) or var(--cg-card-radius) | 12px, 24px |
| Shadows | var(--cg-elevation-...) or var(--cg-card-shadow) | Raw box-shadow values |
| Font family | var(--cg-font-sans) or var(--cg-font-mono) | 'Arial', sans-serif |
Colors: --cg-color-surface-dim, --cg-color-surface,
--cg-color-surface-bright, --cg-color-surface-container-lowest,
--cg-color-surface-container-low, --cg-color-surface-container,
--cg-color-surface-container-high, --cg-color-surface-container-highest,
--cg-color-on-surface, --cg-color-on-surface-muted, --cg-color-primary,
--cg-color-primary-container, --cg-color-on-primary,
--cg-color-on-primary-container, --cg-color-secondary,
--cg-color-secondary-container, --cg-color-on-secondary,
--cg-color-on-secondary-container, --cg-color-tertiary,
--cg-color-tertiary-container, --cg-color-on-tertiary,
--cg-color-on-tertiary-container, --cg-color-error,
--cg-color-error-container, --cg-color-on-error,
--cg-color-on-error-container, --cg-color-outline,
--cg-color-outline-variant
Typography: --cg-font-sans, --cg-font-mono,
--cg-text-display-{lg,md,sm}-{size,weight},
--cg-text-headline-{lg,md,sm}-{size,weight},
--cg-text-title-{lg,md,sm}-{size,weight},
--cg-text-body-{lg,md,sm}-{size,weight},
--cg-text-label-{lg,md,sm}-{size,weight}
Spacing (4px grid): --cg-sp-0 through --cg-sp-16
Radius: --cg-radius-{xs,sm,md,lg,xl,full}
Elevation: --cg-elevation-{1,2,3}
Motion: --cg-motion-duration-{short,medium,long},
--cg-motion-easing-{standard,decel,accel}
Component tokens: Card: --cg-card-{bg,radius,padding,shadow}, Button:
--cg-button-{radius,padding,bg,color,font-size,font-weight}, Input:
--cg-input-{bg,border,radius,padding,color,placeholder}, Badge:
--cg-badge-{bg,color,radius,padding,font-size}, Divider:
--cg-divider-{color,thickness,style}
Expressive: --cg-border-{style,width},
--cg-heading-{transform,letter-spacing},
--cg-img-{radius,border,shadow,filter}, --cg-hover-{scale,brightness,shadow}
Header,
MetricsGrid, ForecastCard, etc.Google Material Symbols Outlined is available via a web font:
<span className="material-symbols-outlined" style={{ fontSize: "20px" }}>
search
</span>
CRITICAL: DO NOT import third-party icon packages. You do not have
lucide-react, heroicons, or react-icons installed. Using them will break
the build. ONLY use the material-symbols-outlined span.
Components should be interactive where appropriate. Use useState, useEffect
with cleanup. Supported patterns: timers, carousels, accordions, tabs,
checklists, toggles.
Never use Date.now(), Math.random(), or new Date() in default parameters.
Compute once at module level or use useState(() => ...).
Your React app is one segment of a wider orchestrated journey. Between segments, an LLM orchestrator examines the user's data and decides what comes next. This means:
Each state gets its own component file named after the state:
App.jsx — shell that renders the initial stateviews/InputRequirements.jsx — one view per stateviews/SelectModels.jsxviews/DetailedComparison.jsxviews/DecisionReport.jsxcomponents/*.jsx — shared sub-components (reusable across views)styles.css — shared stylesEach view component receives two props:
data — the journey context relevant to this stateonTransition — callback for state transitions (wired to
window.opalSDK.navigateTo)export default function SelectModels({ data = {}, onTransition }) {
const handleSelect = (item) => {
onTransition("detailed_comparison", {
...data,
shortlist: [...(data.shortlist || []), item],
});
};
// ...
}
components/. Headers, cards, buttons used
across multiple views should be extracted.readFile to load shared workspace data.React, useState, useEffect, useRef, useCallback, useMemo,
useContext, useReducer, useLayoutEffect, memo, forwardRef,
createContext, Fragment
Be creative and visually impressive.