name: f0-move-component description: Use when moving an F0 component from one Storybook section to another. Covers folder moves, deprecated re-exports for backward compatibility, and bulk import updates across the codebase.
f0-move-component
PURPOSE: Step-by-step instructions for moving a component between sections in the F0 design system (
packages/react/src/), preserving backward compatibility and keeping all imports consistent.
Storybook Sections Reference
All sections are registered in packages/react/.storybook/main.ts via { directory, titlePrefix } entries.
| Source folder | titlePrefix |
Notes |
|---|---|---|
src/components |
Components |
All public F0 components |
src/patterns |
Patterns |
— |
src/kits |
Kits |
— |
src/experimental |
Experimental |
Legacy only — do NOT add new components here |
src/layouts |
Layouts |
Page layout components |
src/lib |
Library |
Internal utilities and providers |
src/hooks |
Library |
Public exported hooks |
src/sds |
SDS |
Satellite design systems |
src/examples |
Examples |
Full-app example stories |
src/ui |
🔒 Internal |
Radix/shadcn primitive wrappers — not public |
If the destination section is not in this table, see Adding a new section before continuing.
Workflow
Step 1 — Move the entire folder
Move the component folder as-is. Do not rename anything inside it.
mv packages/react/src/<source>/<ComponentName> packages/react/src/<destination>/<ComponentName>
Example — moving F0Button from components to kits:
mv packages/react/src/components/F0Button packages/react/src/kits/F0Button
Step 2 — Do NOT rename any story's title field
The titlePrefix in main.ts is prepended automatically by Storybook. The title field inside each .stories.tsx file only needs the component-relative path — never the section name.
// BEFORE move — lives in src/components/F0Button/__stories__/F0Button.stories.tsx
// Storybook renders it as: Components/F0Button
const meta: Meta<typeof F0Button> = {
title: "F0Button", // ← do not touch this
...
}
// AFTER move — now lives in src/kits/F0Button/__stories__/F0Button.stories.tsx
// Storybook renders it as: Kits/F0Button ← section changes automatically
const meta: Meta<typeof F0Button> = {
title: "F0Button", // ← still unchanged
...
}
Step 3 — Add a deprecated re-export in the source section's exports.ts
This preserves backward compatibility for any consumer importing from the old path.
Locate (or create) the exports.ts file at the root of the source section folder and add a @deprecated re-export pointing to the new location.
Pattern:
/** @deprecated <ComponentName> has moved to @/<destination>/<ComponentName>. Import from there instead. */
export * from "../<destination>/<ComponentName>"
Example — after moving F0Button from components to kits:
// packages/react/src/components/exports.ts
/** @deprecated F0Button has moved to @/kits/F0Button. Import from there instead. */
export * from "../kits/F0Button"
If
exports.tsdoes not exist in the source section, create it with only the deprecated re-export line above.
Step 4 — Update all imports across the codebase
4a — Update @/ alias imports
Replace every occurrence of the old @/ alias import path with the new canonical path.
Dry-run first (lists affected files without modifying them):
grep -r "@/<source>/<ComponentName>" packages/react/src \
--include="*.ts" --include="*.tsx" -l
Apply the replacement:
find packages/react/src -type f \( -name "*.ts" -o -name "*.tsx" \) \
-exec sed -i '' 's|@/<source>/<ComponentName>|@/<destination>/<ComponentName>|g' {} +
Example — updating imports after moving F0Button from components to kits:
# Dry-run
grep -r "@/components/F0Button" packages/react/src --include="*.ts" --include="*.tsx" -l
# Apply
find packages/react/src -type f \( -name "*.ts" -o -name "*.tsx" \) \
-exec sed -i '' 's|@/components/F0Button|@/kits/F0Button|g' {} +
The deprecated re-export added in Step 3 means existing consumers outside
packages/react/src(e.g., the Factorial app) continue to work unchanged until they migrate to the new path.
4b — Check and fix relative imports inside the moved component
Files inside the moved component may use relative imports that point to sibling folders
that stayed behind (e.g., ../../../components/Charts/utils/colors). These break silently
because sed on @/ paths does not touch relative paths.
Find broken relative imports inside the moved component:
grep -r "\.\." packages/react/src/<destination>/<ComponentName> \
--include="*.ts" --include="*.tsx" | grep "<source>"
Also check files that were NOT moved but imported FROM the moved component using relative paths:
grep -r "<source>/<ComponentName>" packages/react/src \
--include="*.ts" --include="*.tsx" | grep "\.\."
Fix by converting broken relative paths to @/ alias paths:
# Example: inside the moved component, relative refs to a sibling that stayed behind
find packages/react/src/<destination>/<ComponentName> -type f \( -name "*.ts" -o -name "*.tsx" \) \
-exec sed -i '' 's|\.\./\.\./\.\./\.<source>/SiblingFolder|@/<source>/SiblingFolder|g' {} +
Rule of thumb: after moving, all cross-component imports inside the destination folder should use
@/alias paths, never relative paths that cross folder boundaries outside the component itself.
Step 5 — Verify
Run all checks from packages/react/:
pnpm tsc # type-check — catches broken imports
pnpm lint # lint
pnpm vitest:ci # unit tests
Fix any errors before committing.
Checklist
Use this before opening a PR:
- Component folder moved to destination section
- No
titlefield modified in any.stories.tsxfile -
@deprecatedre-export added tosrc/<source>/exports.ts - Re-export uses
export * from "../<destination>/<ComponentName>" - All
@/<source>/<ComponentName>imports updated to@/<destination>/<ComponentName> - Relative imports inside the moved component inspected — broken cross-boundary relative paths converted to
@/alias paths -
pnpm tscpasses with no errors -
pnpm lintpasses with no errors -
pnpm vitest:cipasses
Adding a new section
If the destination folder is not registered in main.ts, add an entry to the stories array before moving any component:
// packages/react/.storybook/main.ts
stories: [
// ... existing entries ...
{
directory: "../src/<new-folder>",
titlePrefix: "<New Section Label>",
},
],
Create the folder if it does not exist:
mkdir -p packages/react/src/<new-folder>
Then continue from Step 1.
Real-world examples from the codebase
These existing deprecated re-exports follow the same pattern described above and can be used as reference:
// src/components/exports.ts
/** @deprecated UpsellingKit has moved to @/sds/UpsellingKit. Import from there instead. */
export * from "../sds/UpsellingKit/exports"
// src/experimental/exports.ts
/** @deprecated Banners has moved to @/sds/ai/Banners. Import from there instead. */
export * from "../sds/ai/Banners/exports"
// src/ai/exports.ts
/**
* @deprecated This path is deprecated. Import from '@factorial/f0/sds/ai' instead.
* This re-export will be removed in a future version.
*/
export * from "../sds/ai/exports"