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 init first, so the project has an archetype.json. See The CLI.
  • Three dev dependencies and a Chrome. components push uses esbuild, playwright-core and Chrome: Google Chrome if it's installed, otherwise run npx playwright install chromium, or set ARCHETYPE_CHROME to a Chrome binary.
  • Edit rights on components. Your role in the brand has to allow editing components.
Terminal
npm i -D archetype-os@0.3.2 esbuild playwright-core

Define 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:

components/ui/button.archetype.tsx
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" }] },
});
FieldWhat it does
keyThe handle agents and the app use. Lowercase words joined by dashes, like date-picker. Don't change it once published.
nameThe name people see. Required.
descriptionWhat it is and when to use it. At most 400 characters.
platformsWhich surfaces it's for: universal, app, mobile, website, marketing, email.
nestedtrue for a component that is only ever a part of others.
codeWhere the real component lives: import (the path) and export (the name). Agents get this import line.
propertiesThe 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.
renderDraws one example from the property values it is given. Required.
examplesWhich 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

FieldWhat it does
widthWidth of each example in px, for a component that fills its parent.
heightHeight of the window an overlay (a root that is position: fixed) is drawn in. 600 unless set.
viewportWidth of the window examples are drawn in. 1440 unless set.
pathThe address examples are drawn at, which is what usePathname and useSearchParams read, so a nav lights the right row. / unless set.
stateTargetA selector inside the root that Hover, Pressed and Focus are forced on, when the interactive element isn't the root.
partsOther components nested inside this one that can't mark themselves: a selector mapped to that component's key.
frameA className for the box examples are drawn in, for a component that needs its page's variables or fonts.
prefixThe 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:

archetype.json
{
  "url": "https://www.heyarchetype.com",
  "brand": "acme",
  "components": { "css": ["app/globals.css"], "publicUrl": "https://acme.com" }
}

Push to the brand

  1. 1

    Sign in with edits allowed

    Run npx archetype-os login --write once. A plain sign-in is read-only, and the push is refused without edits.

  2. 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. 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:

  1. 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.
  2. Add it to the repository as the secret ARCHETYPE_TOKEN.
  3. Copy /ci/archetype-components.yml from your Archetype address into .github/workflows/. It installs your dependencies and runs the push on pushes to main that 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_brand either.
  • 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

WhatLimit
Description400 characters. A longer one fails the whole push, even with --only.
Properties per component8
Values per property40
Examples per component240
Example HTML / CSS120,000 / 200,000 characters
Component and example names80 characters
Components per brand200

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.