Skip to main content

Documentation Index

Fetch the complete documentation index at: https://subframe-59800133-apg-component-directories.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Subframe now syncs each component as its own directory instead of a single file. This page explains what changed, why, and how to migrate an existing project.

What changed

Previously, each component synced as a single flat file:
src/ui/components/
└─ Button.tsx
Now each component (and page layout) syncs as a directory:
src/ui/components/
└─ Button/
   ├─ Button.tsx   // generated by Subframe — overwritten on every sync
   └─ index.tsx    // yours to edit — wraps and re-exports Button.tsx
Button.tsx is the component Subframe generates, exactly as before. index.tsx is a thin wrapper that re-exports it:
index.tsx
import { Button } from "./Button";

/**
 * Add wrapper components and business logic here.
 * If you modify this file, disable Subframe sync for it to prevent overwrites.
 */

export { Button };
Your imports don’t change. @/ui/components/Button resolves to the directory’s index.tsx, so existing code keeps working.

Why

A per-component directory gives each component a home for everything that should live alongside it in code:
  • index.tsx — a natural, stable place for your own wrapping logic and wrapper components, kept separate from the source Subframe generates.
  • Button.md — component documentation describing what the component is and how it should be used.
  • In the future — generated .stories files for Storybook.
It also makes disabling sync granular. @subframe/sync-disable works per file, so you can freeze your index.tsx while Button.tsx keeps receiving Subframe’s visual updates — instead of having to freeze the whole component.

Adding business logic

index.tsx is where your code goes. To extend a component, edit its index.tsx and add the @subframe/sync-disable marker so the CLI won’t overwrite it on the next sync:
index.tsx
// @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 has no marker, so it keeps syncing — design changes from Subframe still flow in, while your logic in index.tsx is preserved. Anything importing @/ui/components/Button gets your wrapped version automatically, with no import changes.

Migrating an existing project

The CLI migrates your project automatically as you sync. There are no breaking changes — imports are unchanged, so a component moving into a directory doesn’t affect anything that imports it. The one exception is sync-disabled components, which need a quick import review (covered below). Because nothing breaks, you can migrate incrementally — sync a few components at a time, or run a full sync to do everything at once. Flat and nested components coexist fine while you’re partway through.
The migration runs in recent versions of the CLI. The commands below use @subframe/cli@latest so you always get the newest — if you’ve pinned an older @subframe/cli, update it before migrating.
1

Sync your components

Run a full sync to migrate everything at once:
npx @subframe/cli@latest sync --all
Or sync specific components — npx @subframe/cli@latest sync Button Alert — to migrate just those. Either way, the CLI writes the new directory layout and removes the old flat files for the components it syncs.
2

Review any sync-disabled files

If you’d added @subframe/sync-disable to a component file under the old layout, the CLI won’t delete it — instead it moves it into the new directory (e.g. components/Button.tsxcomponents/Button/Button.tsx) and prints a warning listing each moved file.Because the file moved one level deeper, its relative imports may now be wrong — e.g. import { Tooltip } from "./Tooltip" needs to become import { Tooltip } from "../Tooltip". Review each warned file and fix its imports.
3

Commit the change on its own

A full migration touches every component, so the diff is large but mechanical — committing it separately keeps it easy to review.
Last modified on May 15, 2026