Developers
Component sync
Define your React components in *.archetype.tsx files and push them to the brand, so Archetype holds what your code ships.
Your repository's components are the real ones. Component sync draws them from your code and keeps them in the brand, so designers see what actually ships and agents are told to import your component instead of copying its markup.
Before you start
- React components. Sync draws React components only, for now.
- The CLI, set up. Run
npx archetype-os initfirst, so the project has anarchetype.json. See The CLI. - Three dev dependencies and a Chrome.
components pushusesesbuild,playwright-coreand Chrome: Google Chrome if it's installed, otherwise runnpx playwright install chromium, or setARCHETYPE_CHROMEto a Chrome binary. - Edit rights on components. Your role in the brand has to allow editing components.
npm i -D archetype-os@0.3.2 esbuild playwright-coreDefine a component
Put one file beside each component, named *.archetype.tsx (.jsx, .ts and .js work too). It says what the component is, which properties it varies along, and how to draw each example:
import { defineComponent } from "archetype-os/components";
import { Button } from "./button";
export default defineComponent({
key: "button",
name: "Button",
description: "The one button. Primary is only for Save on unsaved work.",
code: { import: "@/components/ui/button", export: "Button" },
properties: {
Variant: { values: ["Secondary", "Primary", "Danger"], prop: "variant" },
Size: { values: ["Medium", "Large"], prop: "size", map: { Medium: "md", Large: "lg" } },
State: ["Default", "Hover", "Pressed", "Focus", "Disabled"],
},
render: ({ Variant, Size, State }) => (
<Button
variant={Variant.toLowerCase()}
size={Size === "Large" ? "lg" : "md"}
disabled={State === "Disabled"}
>
Save
</Button>
),
examples: { matrix: ["Variant", "State"], include: [{ Size: "Large" }] },
});| Field | What it does |
|---|---|
key | The handle agents and the app use. Lowercase words joined by dashes, like date-picker. Don't change it once published. |
name | The name people see. Required. |
description | What it is and when to use it. At most 400 characters. |
platforms | Which surfaces it's for: universal, app, mobile, website, marketing, email. |
nested | true for a component that is only ever a part of others. |
code | Where the real component lives: import (the path) and export (the name). Agents get this import line. |
properties | The axes it varies along, in order. A list of values, the first being the default, or { values, default, prop, map }. prop names the component prop the property sets, and map gives the prop's value for each property value when they differ. |
render | Draws one example from the property values it is given. Required. |
examples | Which combinations to draw. matrix crosses the properties it names (the rest stay at their default), and include adds single combinations. Leave both out to draw each value of each property, one property at a time. |
A property named State is the interaction state. Hover, Pressed, Active, Focus and Focus visible are drawn for real, by forcing the pseudo-class on the component in the browser, so render never fakes them. Every other state, such as Disabled, Loading or Error, is drawn by render from the component's own props.
Settings for harder components
| Field | What it does |
|---|---|
width | Width of each example in px, for a component that fills its parent. |
height | Height of the window an overlay (a root that is position: fixed) is drawn in. 600 unless set. |
viewport | Width of the window examples are drawn in. 1440 unless set. |
path | The address examples are drawn at, which is what usePathname and useSearchParams read, so a nav lights the right row. / unless set. |
stateTarget | A selector inside the root that Hover, Pressed and Focus are forced on, when the interactive element isn't the root. |
parts | Other components nested inside this one that can't mark themselves: a selector mapped to that component's key. |
frame | A className for the box examples are drawn in, for a component that needs its page's variables or fonts. |
prefix | The root's class in the captured CSS. The key unless set. |
width, height and path can also be a function of the property values. An entry in include can set $name, $width, $height, $viewport, $frame and $path for that one example.
If your components rely on global stylesheets, list them in archetype.json. publicDir names the folder your site serves at its root (public unless set), and publicUrl where that folder is online, so stored markup points there:
{
"url": "https://www.heyarchetype.com",
"brand": "acme",
"components": { "css": ["app/globals.css"], "publicUrl": "https://acme.com" }
}Push to the brand
- 1
Sign in with edits allowed
Run
npx archetype-os login --writeonce. A plain sign-in is read-only, and the push is refused without edits. - 2
See what would change
Run
npx archetype-os components push --dry-run. Archetype answers with what the push would add, change and remove, and changes nothing. - 3
Send it
Run
npx archetype-os components push.
The push bundles your definition files with your project's own setup: its tsconfig paths, its React and its PostCSS config, so Tailwind and CSS modules compile as they do in your build. It draws every example in Chrome and reads back the CSS that actually applies to each element. A portal aimed at <body>, such as a dialog's, lands in the example, drawn open.
The brand gets one change per push, which one Undo change in History takes back. A push applies straight away rather than waiting as a draft: it's code that was already reviewed. Components your repository no longer defines are removed, unless you pass --keep or push only some with --only.
--out <file> writes what would be sent to a file instead, with no sign-in. See The CLI for every flag.
Push from CI
Run the push on every merge so the brand never falls behind the code:
- In Settings → Agents, on the API tab, an admin creates a key with Sync components from code ticked. The sync is made as the person who created the key, and stops working if they can no longer edit components.
- Add it to the repository as the secret
ARCHETYPE_TOKEN. - Copy
/ci/archetype-components.ymlfrom your Archetype address into.github/workflows/. It installs your dependencies and runs the push on pushes tomainthat touch your components or styles.
What shows up in Archetype
- Synced from code sits where Edit would be on the component's page, with the commit it came from. It links to the definition file at that commit, and its tooltip names the repository, the file and when it last synced.
- The component is locked in Archetype. Comments stay open, but edits happen in the repository and the next push brings them in. Agents can't change it with
update_brandeither. - When only some of the brand's components came from code, the Components grid shows a filter with their count (for example 12 from code), and Show all to go back.
- Agents reading the brand over MCP get your import line and, where properties map to props, each example's usage, such as
<Button variant="primary" />.
Limits
| What | Limit |
|---|---|
| Description | 400 characters. A longer one fails the whole push, even with --only. |
| Properties per component | 8 |
| Values per property | 40 |
| Examples per component | 240 |
| Example HTML / CSS | 120,000 / 200,000 characters |
| Component and example names | 80 characters |
| Components per brand | 200 |
Two definition files can't share a key, and every definition needs a key, a name and a render function.
Next steps
Something missing or wrong on this page? Write to support@heyarchetype.com.