> ## Documentation Index
> Fetch the complete documentation index at: https://subframe-59800133-apg-edit-updates.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Syncing components

> Keep your design system in sync between Subframe and your codebase.

Subframe is the single source of truth for your design system. Components you build in Subframe sync directly to your codebase — same code, same behavior.

<Frame caption="You can copy the sync command from the navbar in Subframe.">
  <video autoPlay muted loop playsInline controls className="w-full" src="https://hevpkratkeuc60w7.public.blob.vercel-storage.com/sync-components-qNU25B0X4BKttDuiXdqf4h6WEt0fYO.webm" />
</Frame>

## Using the CLI

Sync all components to your codebase with the CLI:

```bash theme={null}
npx @subframe/cli@latest sync --all
```

To sync specific components:

```bash theme={null}
npx @subframe/cli@latest sync Button Alert Accordion
```

The CLI pulls all components into your configured directory in your [project settings](/learn/projects/project-settings).

Note that **sync is one-way** from Subframe to your codebase. Local changes to synced files will be overwritten on the next sync. If you don't want to overwrite local changes, you can [disable sync](/concepts/syncing-components#disabling-sync) for the component. In the future, we'll support syncing code changes back to Subframe.

## Adding business logic

Subframe components are presentational — they handle layout, styling, and UI interactions like accordion open/close, but avoid adding product-specific application logic like API calls or state management.

Under the hood, we use [Radix](https://www.radix-ui.com/) for interactive behavior, which gives you accessible, well-tested interactions out of the box.

### Adding handlers and attributes

All components pass through props to the top-level element. This means you can add `data-*` attributes, `tabIndex`, `onClick`, or any other standard HTML attribute directly to any component.

```tsx theme={null}
<Button onClick={handleSubmit} tabIndex={0} data-testid="submit-btn">
  Submit
</Button>
```

### Using slots to access nested props

It's common for components to contain interactive elements within them—like a card with a button. Slots let you access these nested elements' props:

```tsx theme={null}
import { TrackCard } from "@/ui/components/TrackCard"

function MyTrackCard({ onFavorite }) {
  return (
    <TrackCard
      title="Song Title"
      artist="Artist Name"
      // Slots let you access nested props, without top-level object props
      // [!code highlight:8]
      favoriteButtonSlot={
        <TrackCard.FavoriteButton
          icon="FeatherHeart"
          onClick={() => {
            // Add any business logic here
          }}
        />
      }
    />
  )
}
```

The flexibility of slots lets you pass handlers, custom icons, or even swap in entirely different components. You can create [slots ↗](https://www.youtube.com/watch?v=7C9cRkvKbQY) using Subframe's component editor.

### Wrapping components

For logic not supported by Radix or slots, wrap the Subframe component.

Each component syncs as a directory: `components/Button/Button.tsx` is the source Subframe generates, and `components/Button/index.tsx` is a wrapper that re-exports it. The wrapper is yours to edit — add your logic there and mark it with `@subframe/sync-disable` so the CLI won't overwrite it:

```tsx components/Button/index.tsx theme={null}
// @subframe/sync-disable
import { Button as ButtonComponent } from "./Button"

export function Button({ onSubmit, ...props }) {
  const [loading, setLoading] = useState(false)

  async function handleClick() {
    setLoading(true)
    await onSubmit()
    setLoading(false)
  }

  return <ButtonComponent {...props} loading={loading} onClick={handleClick} />
}
```

`Button.tsx` keeps syncing, so Subframe stays the source of truth for the visual layer — while `index.tsx` gives you the flexibility of code to add any business logic. Anything importing `@/ui/components/Button` gets your wrapped version, with no import changes.

See [Component directories](/upgrading/component-directories) for the full layout and migration details.

<Note>In the future, we'll let you add component logic directly in Subframe.</Note>

### Disabling sync

Add the `@subframe/sync-disable` comment anywhere in a file to tell the CLI to skip it on the next sync:

```tsx theme={null}
// @subframe/sync-disable
```

The marker applies per file, so which file you add it to matters:

* **The wrapper `index.tsx`** — the usual choice once you've added [wrapping logic](/concepts/syncing-components#wrapping-components). Your wrapper is preserved, while `Button.tsx` keeps receiving Subframe's updates.
* **The main `Button.tsx`** — a last resort that fully freezes the file Subframe generates, so the component stops receiving updates. Prefer wrapping in `index.tsx` instead.

You can alternatively copy/paste component code directly from Subframe if you need a one-off version.
