feature-architecture
Skill0 starsChanged 4 months agoMITView on GitHub
Feature folder structure and architecture for React + TypeScript admin apps. Use this when creating a new feature, adding a major section, or when asked how to organize a feature's files. Covers the canonical folder layout (components, hooks, services, types, schemas, constants, utils), what goes where, and index export conventions.
In this repository12 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- frontend-auditSkill
- frontend-i18nSkill
- mui-themeSkill
- react-coreSkill
- react-standardSkill
- table-mrtSkill
- tanstack-formSkill
- tanstack-querySkill
- tanstack-routerSkill
- flutter-clean-arch-getxSkill
- flutter-flavorsSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 63
All TypeScript types for the feature — API response shapes, filter params, form data types.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Feature Architecture
Core Invariants (always enforced — never violate)
- Route files NEVER go inside
features/— they live exclusively insrc/routes/. A feature folder contains zero route files. - Feature folders contain only:
components/,hooks/,services/,schemas/,types/,constants/,utils/,index.ts. - Nothing in
features/may import fromsrc/routes/.
Project Top-Level Structure
src/
├── assets/ # Static assets, locale JSON files
├── core/ # App-level infrastructure (axios, queryClient, router, i18n, contexts)
├── features/ # Business logic, UI components, hooks, services — one folder per domain
├── routes/ # ALL route files — file-based routing only, never inside features/
└── shared/ # Cross-feature shared components, hooks, types, utilsCanonical Feature Folder Layout
features/
└── your-feature/
├── components/
│ ├── YourFeatureList.tsx # Data table component
│ ├── YourFeatureDetail.tsx # Detail view
│ ├── YourFeatureForm.tsx # Create/Update form
│ ├── YourFeatureFilters.tsx # Filter accordion
│ ├── YourFeatureFormSections/ # Form sub-sections (optional)
│ │ ├── BasicInfoSection.tsx
│ │ └── index.ts
│ └── index.ts # Re-exports all public components
├── constants/
│ └── your-feature.constants.ts # Endpoints, query keys, option lists
├── hooks/
│ ├── useYourFeatures.ts # List query
│ ├── useYourFeature.ts # Single item query
│ ├── useCreateYourFeature.ts # Create mutation
│ ├── useUpdateYourFeature.ts # Update mutation
│ ├── useDeleteYourFeature.ts # Delete mutation
│ └── index.ts # Re-exports all hooks
├── schemas/
│ └── your-feature.schema.ts # Zod schemas (search + form) — imported by route files
├── services/
│ ├── your-feature.service.ts # Axios service object
│ └── index.ts
├── types/
│ └── index.ts # All TypeScript types/interfaces
├── utils/ # Pure utility functions (optional)
│ └── index.ts
└── index.ts # Public API of the featureWhat Goes Where
types/index.ts
All TypeScript types for the feature — API response shapes, filter params, form data types.
export interface YourFeature {
id: number
name: string | null
// ...
}
export interface YourFeaturesResponse {
items: YourFeature[]
total: number
}
export interface YourFeatureFilterParams {
page?: number
page_size?: number
search?: string
// ...
}constants/your-feature.constants.ts
Three things: API endpoint strings, TanStack Query key factory, and option arrays.
export const YOUR_FEATURE_ENDPOINTS = {
BASE: '/v1/admin/your-features',
DETAIL: (id: number) => `/v1/admin/your-features/${id}`,
UPDATE: (id: number) => `/v1/admin/your-features/${id}`,
} as const
export const YOUR_FEATURE_QUERY_KEYS = {
all: ['your-features'] as const,
lists: () => [...YOUR_FEATURE_QUERY_KEYS.all, 'list'] as const,
list: (params: YourFeatureFilterParams) => [...YOUR_FEATURE_QUERY_KEYS.lists(), params] as const,
details: () => [...YOUR_FEATURE_QUERY_KEYS.all, 'detail'] as const,
detail: (id: number) => [...YOUR_FEATURE_QUERY_KEYS.details(), id] as const,
}
export const STATUS_OPTIONS = [
{ value: 'active', label: 'yourFeature.statusOptions.active' },
{ value: 'inactive', label: 'yourFeature.statusOptions.inactive' },
] as constservices/your-feature.service.ts
Object with async methods, each taking an optional signal?: AbortSignal for cancellation.
import axiosInstance from '@/core/api/axios.config'
export const yourFeatureService = {
async getAll(params?: YourFeatureFilterParams, signal?: AbortSignal) {
const { data } = await axiosInstance.get<YourFeaturesResponse>(
YOUR_FEATURE_ENDPOINTS.BASE, { params, signal }
)
return data
},
async getById(id: number, signal?: AbortSignal) {
const { data } = await axiosInstance.get(YOUR_FEATURE_ENDPOINTS.DETAIL(id), { signal })
return data
},
async update(id: number, payload: YourFeatureUpdateRequest, signal?: AbortSignal) {
const { data } = await axiosInstance.put(YOUR_FEATURE_ENDPOINTS.UPDATE(id), payload, { signal })
return data
},
}hooks/ — one file per operation
Thin wrappers around TanStack Query. See tanstack-query skill for full patterns.
schemas/your-feature.schema.ts
Zod schemas for both search params and form validation. Search schemas are imported by route files. Form schemas use factory functions that accept t: TFunction. See tanstack-form and tanstack-router skills.
import { fallback } from '@tanstack/zod-adapter'
// Search schema — imported by route file for validateSearch
export const yourFeatureSearchSchema = z.object({
page: fallback(z.number(), 1).default(1),
page_size: fallback(z.number(), 20).default(20),
search: z.string().optional(),
})
export type YourFeatureSearch = z.infer<typeof yourFeatureSearchSchema>
// Form schema — factory because it needs translated error messages
export const getYourFeatureSchema = (t: TFunction) => z.object({
name: z.string().min(1, t('validation.required')),
})
export type YourFeatureFormData = z.infer<ReturnType<typeof getYourFeatureSchema>>components/index.ts
Named re-exports only — keeps route file imports clean.
export { YourFeatureList } from './YourFeatureList'
export { YourFeatureDetail } from './YourFeatureDetail'
export { YourFeatureFilters } from './YourFeatureFilters'index.ts (feature root)
Barrel export of the feature's public API. Route files import from here or from specific sub-paths.
export * from './components'
export * from './hooks'
export * from './types'Route Files — Where They Live
Route files are not part of the feature folder. They live in src/routes/ following the URL structure:
src/routes/
└── _layout/
└── your-section/
└── your-feature/
├── index.tsx # list page
├── create.tsx # create page
└── $featureId/
├── index.tsx # detail page
└── edit.tsx # edit pageRoute files are thin — they only wire URL params/search to feature components. See tanstack-router skill for the full route file pattern.
Form Sections Pattern
For complex forms, split fields into section components under YourFeatureFormSections/. Each section receives form as a typed prop:
interface BasicInfoSectionProps {
form: YourFeatureFormApi
}
export function BasicInfoSection({ form }: BasicInfoSectionProps) {
return (
<FormSectionCard title="Basic Info">
<form.AppField name="name">
{(field) => <field.TextField label={t('feature.name')} />}
</form.AppField>
</FormSectionCard>
)
}The YourFeatureFormApi type is defined in the schema file:
export interface YourFeatureFormApi extends AppFormApi<YourFeatureFormData> {}Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

