mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
6.4 KiB
6.4 KiB
Frontend Development Guidelines
TypeScript LSP Navigation (USE FIRST)
When investigating or editing TypeScript/React code, always use the TypeScript language server tools for accurate navigation:
- Find component/function definitions:
mcp__typescript-language-server__definition ComponentName- Example:
mcp__typescript-language-server__definition LoginPage
- Example:
- Find all usages:
mcp__typescript-language-server__references ComponentName- Example:
mcp__typescript-language-server__references useAuthenticate
- Example:
- Get type information:
mcp__typescript-language-server__hover site/src/pages/LoginPage.tsx 42 15 - Check for errors:
mcp__typescript-language-server__diagnostics site/src/pages/LoginPage.tsx - Rename symbols:
mcp__typescript-language-server__rename_symbol site/src/components/Button.tsx 10 5 PrimaryButton - Edit files:
mcp__typescript-language-server__edit_filefor multi-line edits
Bash commands
pnpm dev- Start Vite development serverpnpm storybook --no-open- Run storybook testspnpm test- Run jest unit testspnpm test -- path/to/specific.test.ts- Run a single test filepnpm lint- Run complete linting suite (Biome + TypeScript + circular deps + knip)pnpm lint:fix- Auto-fix linting issues where possiblepnpm playwright:test- Run playwright e2e tests. When running e2e tests, remind the user that a license is required to run all the testspnpm format- Format frontend code. Always run before creating a PR
Components
- MUI components are deprecated - migrate away from these when encountered
- Use shadcn/ui components first - check
site/src/componentsfor existing implementations. - Do not use shadcn CLI - manually add components to maintain consistency
- The modules folder should contain components with business logic specific to the codebase.
- Create custom components only when shadcn alternatives don't exist
Styling
- Emotion CSS is deprecated. Use Tailwind CSS instead.
- Use custom Tailwind classes in tailwind.config.js.
- Tailwind CSS reset is currently not used to maintain compatibility with MUI
- Responsive design - use Tailwind's responsive prefixes (sm:, md:, lg:, xl:)
- Do not use
dark:prefix for dark mode
Tailwind Best Practices
- Group related classes
- Use semantic color names from the theme inside
tailwind.config.jsincludingcontent,surface,border,highlightsemantic tokens - Prefer Tailwind utilities over custom CSS when possible
General Code style
- Use ES modules (import/export) syntax, not CommonJS (require)
- Destructure imports when possible (eg. import { foo } from 'bar')
- Prefer
for...ofoverforEachfor iteration - Biome handles both linting and formatting (not ESLint/Prettier)
- Always use react-query for data fetching. Do not attempt to manage any data life cycle manually. Do not ever call an
APIfunction directly within a component.
Workflow
- Be sure to typecheck when you're done making a series of code changes
- Prefer running single tests, and not the whole test suite, for performance
- Some e2e tests require a license from the user to execute
- Use pnpm format before creating a PR
- ALWAYS use TypeScript LSP tools first when investigating code - don't manually search files
Pre-PR Checklist
pnpm check- Ensure no TypeScript errorspnpm lint- Fix linting issuespnpm format- Format code consistentlypnpm test- Run affected unit tests- Visual check in Storybook if component changes
Migration (MUI → shadcn) (Emotion → Tailwind)
Migration Strategy
- Identify MUI components in current feature
- Find shadcn equivalent in existing components
- Create wrapper if needed for missing functionality
- Update tests to reflect new component structure
- Remove MUI imports once migration complete
Migration Guidelines
- Use Tailwind classes for all new styling
- Replace Emotion
cssprop with Tailwind classes - Leverage custom color tokens:
content-primary,surface-secondary, etc. - Use
classNamewithclsxfor conditional styling
React Rules
1. Purity & Immutability
- Components and custom Hooks must be pure and idempotent—same inputs → same output; move side-effects to event handlers or Effects.
- Never mutate props, state, or values returned by Hooks. Always create new objects or use the setter from useState.
2. Rules of Hooks
- Only call Hooks at the top level of a function component or another custom Hook—never in loops, conditions, nested functions, or try / catch.
- Only call Hooks from React functions. Regular JS functions, classes, event handlers, useMemo, etc. are off-limits.
3. React orchestrates execution
- Don’t call component functions directly; render them via JSX. This keeps Hook rules intact and lets React optimize reconciliation.
- Never pass Hooks around as values or mutate them dynamically. Keep Hook usage static and local to each component.
4. State Management
- After calling a setter you’ll still read the previous state during the same event; updates are queued and batched.
- Use functional updates (setX(prev ⇒ …)) whenever next state depends on previous state.
- Pass a function to useState(initialFn) for lazy initialization—it runs only on the first render.
- If the next state is Object.is-equal to the current one, React skips the re-render.
5. Effects
- An Effect takes a setup function and optional cleanup; React runs setup after commit, cleanup before the next setup or on unmount.
- The dependency array must list every reactive value referenced inside the Effect, and its length must stay constant.
- Effects run only on the client, never during server rendering.
- Use Effects solely to synchronize with external systems; if you’re not “escaping React,” you probably don’t need one.
6. Lists & Keys
- Every sibling element in a list needs a stable, unique key prop. Never use array indexes or Math.random(); prefer data-driven IDs.
- Keys aren’t passed to children and must not change between renders; if you return multiple nodes per item, use
<Fragment key={id}>
7. Refs & DOM Access
- useRef stores a mutable .current without causing re-renders.
- Don’t call Hooks (including useRef) inside loops, conditions, or map(). Extract a child component instead.
- Avoid reading or mutating refs during render; access them in event handlers or Effects after commit.