Cómo usar
- Copia el comando y revisa el paquete antes de instalarlo en tu entorno de agente.
- Consulta las herramientas, credenciales y entornos necesarios en el documento original.
- Describe las entradas y el resultado deseado; contrasta la salida con las fuentes.
npx skills add https://github.com/jason23452/my-skill --skill react-vite-feature-basedAntes de empezar
- La instalación no incluye suscripción al modelo, cuota API ni permisos sobre tus archivos.
- Las estrellas e instalaciones reflejan actividad, no una prueba independiente de calidad o seguridad.
Contenido del repositorio de origen con títulos, ejemplos, código, tablas, enlaces e imágenes preservados.
React Vite Feature-Based
OpenCode Greenfield Bootstrap Metadata
{
"role": "frontend",
"category": "framework",
"framework": "react-vite",
"order": 10,
"packageManager": "pnpm",
"scaffoldCommand": [
"pnpm create vite . --template react-ts --no-interactive",
"if test -f .opencode/skills/react-vite-feature-based/scripts/bootstrap-01-02.cjs; then node .opencode/skills/react-vite-feature-based/scripts/bootstrap-01-02.cjs; else node ${OPENCODE_PROJECT_SKILLS_PRESEEDED_DIR:-/app/.opencode/skills}/react-vite-feature-based/scripts/bootstrap-01-02.cjs; fi",
"if test -f .opencode/skills/react-vite-feature-based/scripts/bootstrap-01-03.cjs; then node .opencode/skills/react-vite-feature-based/scripts/bootstrap-01-03.cjs; else node ${OPENCODE_PROJECT_SKILLS_PRESEEDED_DIR:-/app/.opencode/skills}/react-vite-feature-based/scripts/bootstrap-01-03.cjs; fi",
"if test -f .opencode/skills/react-vite-feature-based/scripts/bootstrap-00-pnpm-allow-builds.cjs; then node .opencode/skills/react-vite-feature-based/scripts/bootstrap-00-pnpm-allow-builds.cjs; else node ${OPENCODE_PROJECT_SKILLS_PRESEEDED_DIR:-/app/.opencode/skills}/react-vite-feature-based/scripts/bootstrap-00-pnpm-allow-builds.cjs; fi",
"pnpm install --frozen-lockfile=false"
],
"verificationCommands": [
"pnpm build"
],
"runtimeSmokeCommand": "if test -f .opencode/skills/react-vite-feature-based/scripts/runtime-smoke-sandbox.cjs; then node .opencode/skills/react-vite-feature-based/scripts/runtime-smoke-sandbox.cjs --cwd \"$PWD\" --port $PORT; else node ${OPENCODE_PROJECT_SKILLS_PRESEEDED_DIR:-/app/.opencode/skills}/react-vite-feature-based/scripts/runtime-smoke-sandbox.cjs --cwd \"$PWD\" --port $PORT; fi",
"runtimeSmokeHealthUrl": "http://127.0.0.1:$PORT/__opencode_health.txt"
}Runtime smoke in OpenCode Project Flow must use scripts/runtime-smoke-sandbox.cjs. Do not run Vite dev directly from /workspace for smoke checks; Docker bind mounts can make dev-server readiness unreliable. The bootstrap script creates public/__opencode_health.txt so liveness does not depend on app rendering.
Package Manager
Detect the package manager from the lockfile and keep using it:
pnpm-lock.yaml:pnpmpackage-lock.json:npmyarn.lock:yarnbun.lockorbun.lockb:bun
For greenfield bootstrap, default to pnpm.
Project Layout
Use this feature-based layout:
src/
app/
AppRouter.tsx
features/
home/
router/
index.tsx
components/
hooks/
types/
assets/
<feature-name>/
router/
index.tsx
<child-route>/
index.tsx
[param]/
index.tsx
components/
hooks/
types/
assets/
shared/
components/
ui/
layout/
hooks/
types/
assets/
App.tsx
index.css
main.tsxTreat this folder layout as the fixed architecture contract. Keep the top-level ownership boundaries and route file shape stable unless the user explicitly asks for a different architecture.
Implementation details remain flexible. Adapt the router library, route config style, UI package, styling system, state management, data fetching, and component internals to the existing project.
Architecture Invariants
Hardcode only these architecture rules:
src/app,src/features, andsrc/sharedownership boundaries.src/features/home/router/index.tsxas the root route entry for/.- Index-based route segment folders under
features/<feature-name>/router/. src/app/AppRouter.tsxas the place that composes app-level shared chrome with feature router entries.shared/components/ui/for reusable primitives and wrappers;shared/components/layout/for reusable app chrome.
Keep these choices adaptable to the project:
- Router library and route object, JSX, or generated route style.
- UI package, styling system, state management, data fetching, and API client.
- Feature names other than
home, route names, product copy, mock data, and business flows. - Exact shared component slots, prop names, config shapes, and component internals.
Required Root Feature
Always create and keep src/features/home/router/index.tsx as the root route entry.
src/features/home/router/index.tsx -> "/"The home feature folder name is an implementation feature name, not the URL path. Do not create /home for the default homepage unless the user explicitly asks for a separate /home route.
When the project uses a router library, map / to the route entry exported from @/features/home/router in src/app/AppRouter.tsx.
Composition Rule
Build feature UI in three layers, in this order:
shared/components/ui/contains small, high-reuse UI components and UI kit wrappers.features/<feature-name>/components/composes shared components into feature-specific components.features/<feature-name>/router/composes feature components into route/page components.
Do not skip the feature component layer when a route renders feature-specific product UI. A route file should not become the place where global components are assembled directly into product UI. Put that product UI into feature components first, then compose those feature components in the route.
Route files may still render route-only glue directly, such as redirects, guards, suspense or error boundaries, loading fallbacks, simple param wiring, or a temporary placeholder while scaffolding.
Feature UI flow:
shared/components/ui/Button.tsx
-> features/<feature-name>/components/<Feature>Actions.tsx
-> features/<feature-name>/router/index.tsx
-> src/app/AppRouter.tsxAppRouter composition flow:
shared/components/layout/AppShell.tsx
+ features/<feature-name>/router/index.tsx
-> src/app/AppRouter.tsxsrc/app/AppRouter.tsx composes common app-level components with feature router entries. If a component is used by every route or wraps the entire route tree, compose it in AppRouter.tsx. Examples include app shell, root layout, global navigation, global footer, auth boundary, provider boundary, suspense boundary, error boundary, toast host, and global modal host.
This app-level rule does not replace the feature component layer. It only covers components that are genuinely shared by all routes or all routed pages.
Component File Size Budget
When assembling component-heavy UI, keep each route, feature component, shared layout component, and shared UI wrapper within a practical 100-500 line budget whenever feasible.
- Treat
500lines as the split threshold. If a component or route file grows past that, split it into feature sections, layout slots, smaller shared UI wrappers, hooks, data/config objects, or child components. - Treat
100lines as a soft target for meaningful assembled UI, not a minimum. Do not pad or over-split simple route glue, provider wrappers, placeholders, redirects, loading states, or tiny UI primitives just to reach 100 lines. - Route files should stay thin even when under 500 lines. Move reusable cards, forms, tables, dashboards, panels, and product sections into
features/<feature-name>/components/before composing them inrouter/. - Shared layout components such as
AppShell,Sidebar,TopNav,PageFrame, and modal hosts should also stay under 500 lines. Push repeated nav items, panel definitions, action lists, and empty-state copy into config arrays or smaller child components. - If a feature requires many visual states, keep the component responsible for composition and move state derivation, formatting, fixture data, and reusable interaction logic into hooks, helpers, stores, or typed config owned by the same feature.
Persistent app chrome belongs in shared/components/layout/, not in a feature router. Examples include app sidebars, top bars, bottom composers/action bars, context or inspector panels, page frames, and global modal hosts that remain mounted while route content changes. Name promoted components by their app-level responsibility, for example AppSidebar, AppTopbar, AppComposer, AppInspector, or AppPreviewDialog, instead of retaining a feature-specific name.
For routes that share the same app chrome, mount the chrome once in src/app/AppRouter.tsx and switch only the feature route's main content. AppShell may expose named slots such as sidebar, topNav, aside, composer, modalHost, or children; pass shared layout components into those slots from AppRouter.tsx.
Keep AppShell itself generic: accept ReactNode slots, layout flags, callbacks, and configuration from the app boundary. Do not import feature route entries, feature components, feature mock data, route-specific state, or product copy inside the shell.
Keep shared layout components configurable with props, config arrays, slots, or component maps. Do not make shared layout components import feature mock data, feature route state, feature route entries, or feature-specific product components just to assemble themselves. Pass app-level data, labels, empty-state copy, and callbacks from AppRouter.tsx, a shared app store, or another app-level boundary instead.
Use this generality check before moving code into shared/:
- It can be reused without importing from
features/<feature-name>/. - It does not hardcode one feature route, one product flow, one project path, or one mock dataset.
- It accepts feature-specific text, data, navigation targets, and callbacks through props, slots, config, or context.
- It remains useful if the feature name changes.
- It can be tested or rendered with minimal fake data from outside the original feature.
UI Kit Composition
When a project uses a UI package, compose that package through shared/components/ by default:
- Put small, high-reuse components in
shared/components/ui/, for exampleButton,Input,IconButton,Select,Dialog,Tooltip, andMenu. - Wrap UI kit primitives in project-level
ui/components before feature code uses them. - Put reusable variant rules, style defaults, accessibility defaults, and common prop mapping in
shared/components/ui/. - Compose larger shared structures in
shared/components/layout/, for exampleAppShell,Sidebar,TopNav, andPageFrame. - Build layout components from
shared/components/ui/and other shared utilities. - Use config arrays, schemas, or component maps in shared components when common UI must be assembled dynamically.
- Keep raw UI kit imports mostly inside
shared/components/ui/unless the existing codebase already follows a different local pattern. - Feature components should import project-level shared components instead of raw UI kit primitives when the same UI pattern can be reused.
Example:
ui-kit/Button + ui-kit/Menu
-> shared/components/ui/AppActionMenu.tsx
-> features/<feature-name>/components/<Feature>Actions.tsx
-> features/<feature-name>/router/index.tsx
shared/components/ui/Button.tsx
-> shared/components/layout/Sidebar.tsx
-> src/app/AppRouter.tsxDirectory Responsibilities
src/app/
Use src/app/ for application-level composition:
AppRouter.tsxcomposes common app-level components with feature router entries.- Import common app-level layout components from
shared/components/layout/. - Import feature route entries from
features/<feature-name>/router. - Always map the root path
/to@/features/home/router. - Keep cross-feature route registration here.
- Keep app-wide wrappers here, such as app shell, global navigation, providers, auth gates, suspense/error boundaries, toast hosts, and global modal hosts.
- Keep persistent app chrome here so route changes replace only the routed main content.
- If
AppRouter.tsxneeds UI that started inside a feature, promote and rename it intoshared/components/layout/orshared/components/ui/first, then make it props, slot, or config driven. - Do not import
features/<feature-name>/components/directly intoAppRouter.tsx; feature components must be assembled inside that feature's router entry first. - Do not place feature-specific UI implementation here.
features/<feature-name>/router/
Use router/ for index-based route segment files:
router/index.tsxis the feature root route.router/<child-route>/index.tsxis a static child route.router/[param]/index.tsxis a dynamic child route.- Nested child routes continue the same folder pattern.
- Route params, search params, guards, loaders, redirects, and feature-local nested layout decisions.
- Keep persistent all-route chrome in
src/app/AppRouter.tsxandshared/components/layout/. - Composition of one or more components from
features/<feature-name>/components/. - Export route entries for
src/app/AppRouter.tsxto compose.
Example:
src/features/<feature-name>/router/index.tsx
src/features/<feature-name>/router/[id]/index.tsx
src/features/<feature-name>/router/settings/index.tsxKeep route files thin. Reusable UI blocks, cards, forms, tables, and feature business sections belong in components/; move them there and import them into the route. Route files may keep route-only glue such as guards, redirects, loading fallbacks, simple param extraction, and child-route selection.
Prefer route imports like:
import { FeatureHeader } from "@/features/<feature-name>/components/FeatureHeader"
import { FeatureActions } from "@/features/<feature-name>/components/FeatureActions"Avoid route imports like this for feature product UI:
import { Button } from "@/shared/components/ui/Button"If route content needs Button as part of feature UI, create or update a feature component that uses Button, then import that feature component into the route. Direct shared UI imports are acceptable for route-only fallback or shell glue.
features/<feature-name>/components/
Use feature components for product UI that belongs to exactly one feature:
- Compose global/shared components into feature-specific sections.
- Keep feature copy, feature visual structure, feature forms, feature tables, and feature state presentation here.
- A feature component may import from
shared/components,shared/hooks,shared/types, and its own feature folders. - A feature component must not import from
router/.
features/<feature-name>/hooks/
Use feature hooks for behavior that belongs to exactly one feature. Move a hook to shared/hooks/ when at least two features need it or when it is clearly app-wide from the start.
features/<feature-name>/types/
Use feature types for data contracts owned by one feature. Move a type to shared/types/ when it is truly cross-feature or app-wide.
features/<feature-name>/assets/
Use feature assets for images, media, and static files used by one feature only.
shared/
Use shared/ for cross-feature building blocks:
shared/components/ui/: small high-reuse UI components, UI kit wrappers, variants, defaults, and dynamic primitive composition.shared/components/layout/: larger shared layout components assembled fromshared/components/ui/, such as app shell, app sidebar, app top nav, bottom composer/action bar, context or inspector panel, global preview dialog, and page frame.shared/hooks/: cross-feature hooks.shared/types/: cross-feature types.shared/assets/: cross-feature assets.
Do not place feature-specific product copy, business rules, route decisions, or one-off UI in shared/. If only the outer shell is reusable, split the reusable shell into shared/ and keep feature content inside features/<feature-name>/components/.
Greenfield Bootstrap
When creating a new React/Vite project:
- Use Vite React TypeScript scaffold.
- Add the
@import alias for./src. - Add TypeScript paths for
@/*. - Create the feature-based folders.
- Create
src/features/home/router/index.tsxas the required root route entry for/. - Seed the example UI with the required composition flow:
src/shared/components/layout/AppShell.tsx
+ src/features/home/router/index.tsx
-> src/app/AppRouter.tsx
src/shared/components/ui/Button.tsx
-> src/features/home/components/HomeActions.tsx
-> src/features/home/router/index.tsxvite.config.ts must include the alias:
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import { fileURLToPath, URL } from "node:url"
export default defineConfig({
plugins: [react()],
resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) } },
})tsconfig.app.json must include:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}Update Workflow
When adding or refactoring UI:
- Identify whether the code is app-global, reusable primitive UI, shared layout, feature-specific, or route-specific.
- Put small reusable UI in
shared/components/ui/and larger shared layout inshared/components/layout/. - For persistent all-route chrome, add or extend
AppShellslots and pass shared layout components fromsrc/app/AppRouter.tsx. - Put feature product UI in
features/<feature-name>/components/. - Put route/page orchestration in
features/<feature-name>/router/. - Export feature route entries from
features/<feature-name>/router. - Compose common app-level components and feature router entries in
src/app/AppRouter.tsx. - Update imports to use the
@alias for cross-folder imports.
Naming
- Feature folder names use
kebab-case. - Static route segment folder names use
kebab-case. - Dynamic route segment folder names use bracket syntax, for example
[id]or[slug]. - Component and page files use
PascalCase.tsx. - Hook files use
useXxx.ts. - Route files are named
index.tsx. - Prefer route component exports with descriptive
PascalCasenames ending withRoute, for example<Feature>Routeor<Feature><Param>Route, unless the selected router library requires a different export convention. - Rename components when promoting them out of a feature. Use app/shared names such as
AppSidebar,AppTopbar, orAppComposerfor persistent layout chrome, not the original feature name.
Shared Promotion Rule
Move code from a feature folder to shared/ only when:
- At least two features use it, it wraps all routes, or it is clearly app-wide from the start.
- It has no feature-specific copy, route knowledge, business rule, or mock dataset.
- It does not import from
features/. - It accepts feature-specific labels, data, navigation targets, callbacks, and children through props, config, slots, or context.
- Its API is stable enough to be reused without pulling feature dependencies into
shared/.
Verification
After changes, run the package manager's build command:
pnpm build
npm run build
yarn build
bun run buildIf the project has a lint script, run it too:
pnpm lint
npm run lint
yarn lint
bun run lintFor local runtime checks, start the dev server only when useful for the task and provide the local URL.



