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.Documentation Index
Fetch the complete documentation index at: https://docs.subframe.com/llms.txt
Use this file to discover all available pages before exploring further.
What changed
Previously, each component synced as a single flat file:Button.tsx is the component Subframe generates, exactly as before. index.tsx is a thin wrapper that re-exports it:
index.tsx
@/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
.storiesfiles for Storybook, test files, and more.
@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
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.Sync your components
Run a full sync to migrate everything at once: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.Update monorepo exports (if applicable)
If you expose Subframe components from a shared package using the The old
exports field in package.json, update the subpath patterns to point to the new directory layout:package.json
"./ui/components/*.tsx" pattern no longer matches because the flat files have moved into per-component directories. See the monorepo guide for the full setup.Review any sync-disabled files
If you’d added Exported prop types. The directory’s Files the CLI regenerates already export their prop types, so this only affects files you’d frozen with
@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.tsx → components/Button/Button.tsx) and prints a warning listing each moved file.Because the file moved one level deeper, two things need a quick check.Relative imports. Paths that were correct when the file was flat are now off by one level — siblings, shared root files, and cross-directory references all shift:index.tsx re-exports the component (export const Button = ButtonComponent), so TypeScript has to be able to name the types in its signature. If a moved file declares its prop interfaces without export, you’ll get errors like Exported variable 'Button' has or is using name 'ButtonRootProps' … but cannot be named (TS4023). Add export to those interfaces:@subframe/sync-disable.
