frontend-philosophy
Skill175 starsChanged 6 months agoMITView on GitHub
Frontend design philosophy and coding conventions for React/Preact applications. Enforces DHH-inspired simplicity, 8-section component structure, TanStack Query server state management, and strict anti-patterns (no useCallback, no useEffect for state sync, no barrel files, no TypeScript). Use when: (1) scaffolding new frontend projects, (2) building React/Preact components, (3) implementing features with state management, (4) reviewing or refactoring frontend code, (5) setting up project structure and file organization. Triggers on: "scaffold", "new project", "build a component", "create a page", "add a feature", "frontend", "React", "Preact", or any frontend implementation task.
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 4 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 74
Every component follows this order.
- Distinctive: line 131
Only for UI concerns: modals, theme, sidebar, filters, form inputs.
- Distinctive: line 149
Never refetch in children: when parent has the data
- Very specific: line 191
Use cn() from @/lib/utils for class merging
- Distinctive: line 192
Use CVA (class-variance-authority) for component variants
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 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
Frontend Philosophy
Authoritative conventions for all React/Preact work. These are not suggestions — they are the standard. When in doubt, choose the simpler path.
"Every line of code is a liability. The best code is no code. The second best is simple, boring code that obviously works."
Core Principles
- Simplicity over cleverness — Write code a junior dev understands
- Convention over configuration — Follow existing patterns, don't invent
- Optimize for understanding — Code is read 10x more than written
- Embrace the monolith — Colocate related concerns, don't split prematurely
- Performance through simplicity — Fastest code is code that doesn't run
Stack Defaults
| Purpose | Default | Notes |
| Runtime/pkg | Bun | Fallback: npm. Never pnpm/yarn |
| Bundler | Vite | With @/ path alias |
| Framework | React or Preact | + TanStack Query/Router |
| Styling | Tailwind CSS | Utility-first, cn() for merging |
| UI primitives | shadcn/ui pattern | CVA for variants, Radix primitives |
| Server state | TanStack Query | ONLY source of truth for API data |
| Client state | Zustand or Preact Signals | UI state only (modals, theme, filters) |
| Icons | @remixicon/react | Ri*Line for outline, Ri*Fill for solid |
| Types | No TypeScript | .jsx components, .js utilities |
Why No TypeScript
Speed over ceremony. Type gymnastics add grief, not joy. Runtime validation at system boundaries, shared constants, JSDoc for complex functions.
File Conventions
src/
├── components/
│ ├── ui/ # Base UI (shadcn pattern)
│ ├── billing/ # Feature-based grouping
│ ├── support/ # NOT type-based (lists/, forms/, displays/)
│ └── common/ # Shared across features
├── hooks/ # Custom hooks (use* prefix)
├── stores/ # Zustand stores
├── constants/ # Shared constants
├── lib/ # Utilities (cn, formatters)
├── pages/ # Route-level components
└── api/ # API clientNaming rules:
.jsxcomponents,.jsutilities — always- PascalCase component files, camelCase everything else
handle*event handlers,use*hooks,render*render methods- Feature-based folders, never type-based
- No barrel files (index.ts/js re-exports) — ever
- No comments unless logic is truly non-obvious
Component Structure (8 Sections)
Every component follows this order. See references/component-patterns.md for full examples.
const Component = ({ prop1, prop2 }) => {
// 1. Server state (TanStack Query)
const { data, isLoading, error } = useQuery({ ... });
// 2. Global client state (Zustand/signals)
const { theme } = useThemeStore();
// 3. Local state (useState)
const [isOpen, setIsOpen] = useState(false);
// 4. Refs
const formRef = useRef(null);
// 5. Custom hooks
const { isOnline } = useNetworkStatus();
// 6. Derived values (compute in render, no useState)
const isValid = email.includes("@");
const fullName = `${data?.first} ${data?.last}`;
// 7. Effects (DOM sync, subscriptions, analytics ONLY)
useEffect(() => { ... }, [deps]);
// 8. Event handlers
const handleSubmit = () => { ... };
// Return — early returns for loading/error first
if (isLoading) return <Skeleton />;
if (error) return <ErrorMessage error={error} />;
return ( ... );
};State Management Rules
Server State = TanStack Query ONLY
// CORRECT — single source of truth
const { data: users } = useQuery({
queryKey: ["users", filters],
queryFn: () => api.get("/users", { params: filters }),
staleTime: 5 * 60 * 1000,
gcTime: 60 * 60 * 1000, // NOT cacheTime (v5)
});
// WRONG — duplicating server data in local state
const [users, setUsers] = useState([]);
useEffect(() => { fetchUsers().then(setUsers); }, []);Client State = Zustand or Signals
Only for UI concerns: modals, theme, sidebar, filters, form inputs.
Derive, Don't Duplicate
// CORRECT — derive in render
const hasError = error !== null;
const activeUsers = users?.filter(u => u.isActive);
// WRONG — useState for derived values
const [hasError, setHasError] = useState(false);
useEffect(() => setHasError(error !== null), [error]);Data Fetching Hierarchy
- Pages fetch all required data
- Pass data as props to children
- Never refetch in children when parent has the data
Critical Anti-Patterns
These are hard rules, not guidelines.
| NEVER do this | Do this instead |
useEffect for state sync | Move logic to event handlers |
useCallback | Just define the function (almost never needed) |
useState for derived values | Compute in render |
| Duplicate server data in useState | TanStack Query is the source of truth |
| Barrel files (index.ts re-exports) | Import directly from the file |
useMemo without profiling first | Just compute it |
| Prop drilling beyond 2 levels | Composition or Zustand |
| Business logic in components | Extract to hooks or utilities |
| Multiple state management libs | TanStack Query + ONE client state lib |
| Inline styles or global CSS | Tailwind utilities |
| Premature abstraction | Rule of 3 — wait for 3+ use cases |
| Custom wrappers around libraries | Use the library directly |
| Over-commenting | Self-documenting names |
Valid useEffect Uses
Only three:
- DOM synchronization (focus, scroll, resize listeners)
- External subscriptions (WebSocket, event emitters)
- Analytics/logging (page views, tracking)
Everything else belongs in an event handler or TanStack Query.
Formatting
Prettier defaults:
- 2-space indent, double quotes, semicolons
- 100 char print width, trailing commas ES5
- Tailwind plugin for class ordering
UI Patterns
- Buttons: always
rounded-full - Cards: always
rounded-xl - Use
cn()from@/lib/utilsfor class merging - Use CVA (class-variance-authority) for component variants
- Composition over configuration — build with children and slots
data-slotattributes for CSS targeting- Early returns for loading/error states before main render
Code Review Questions
Before shipping, ask:
- Can a junior dev understand this?
- Is this the simplest solution that works?
- Does this follow existing patterns in the codebase?
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- delorenj/ssbnk★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository5 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- insecure-defaultsSkill
- systematic-debuggingSkill
- tddSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
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.

