Setting Up Frontend Project Standards
YAML--- name: setting-up-frontend-project-standards description: Establishes production-grade frontend project tooling and architecture following strict TypeScript, linting, formatting, commit, and dynamic/brand-driven UI conventions used by a Senior Frontend Lead at an International IT Consultancy. Use when scaffolding a new frontend/React project, auditing an existing codebase for best practices, configuring ESLint/Prettier/Husky/commitlint, or reviewing PRs for performance, stability, UX/DX, and reusability concerns. --- # Setting Up Frontend Project Standards
Scaffold a new project with the mandatory toolchain:
Bashpnpm add -D typescript typescript-eslint eslint eslint-config-prettier \ eslint-plugin-prettier eslint-plugin-perfectionist eslint-plugin-sonarjs \ eslint-plugin-unicorn eslint-plugin-unused-imports prettier husky \ lint-staged @commitlint/cli @commitlint/config-conventional @types/node pnpm add @tanstack/react-query axios # If React project: pnpm add -D @types/react eslint-plugin-react-hooks pnpm add react-i18next
Copy .prettierrc, eslint.config.ts exactly as defined in this skill (see reference files below), then wire up husky + lint-staged + commitlint.
Progress:
- Step 1: Initialize TypeScript strictly (
strict: true, no implicit any, no untyped JS allowed) - Step 2: Install mandatory dependencies (always) + optional React dependencies (if applicable)
- Step 3: Add
.prettierrc(tabs, no semi, single quotes, no trailing comma) - Step 4: Add
eslint.config.ts(flat config, TS-based) using the reference config - Step 5: Configure Husky + lint-staged for pre-commit lint/format
- Step 6: Configure commitlint with
@commitlint/config-conventional+ commit-msg hook - Step 7: Architect app for dynamic/backend-driven rendering (config/schema-driven UI, no hardcoded content)
- Step 8: Architect for brand-driven theming (tokens, CSS variables, no hardcoded brand values)
- Step 9: Ensure shareability/reusability (headless components, hooks extraction, no app-coupled logic in shared libs)
- Step 10: Wire
@tanstack/react-queryfor all server state;axiosinstance with interceptors for API layer - Step 11: Run full lint + typecheck + build before considering done
.prettierrc
JSON{ "singleAttributePerLine": true, "trailingComma": "none", "singleQuote": true, "endOfLine": "auto", "useTabs": true, "semi": false, "tabWidth": 2 }
eslint.config.ts — flat config built on typescript-eslint, @eslint/js, with plugins: unused-imports, unicorn (recommended + custom class-member-order/filename-case/no-null rules), perfectionist (recommended-line-length), react-hooks (flat recommended, React projects only), sonarjs (applied to scripts/**/*.mjs), and prettier as the final formatting authority (eslint-config-prettier + eslint-plugin-prettier/recommended). Always globalIgnores build artifacts, lockfiles, and AI-agent tool directories (.claude, .cursor, .gemini, .codex, .opencode, .agents, .github, .nx).
Key enforced rules: unused-imports/no-unused-vars (ignore _-prefixed), no-console (allow table/info/error/warn only), eqeqeq: always, prefer-const, no-debugger, no-alert, no-var, kebab/snake filename casing, no native null (prefer undefined, exceptions for arguments).
commitlint.config.js
JavaScriptmodule.exports = { extends: ['@commitlint/config-conventional'] }
.husky/pre-commit
Bashpnpm lint-staged
.husky/commit-msg
Bashpnpm commitlint --edit "$1"
lint-staged config (package.json)
JSON{ "lint-staged": { "*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"] } }
Example 1:
Input: "New React dashboard project, needs multi-brand white-labeling and server-driven widgets."
Output: Scaffold with full mandatory + React optional deps. Theme via CSS variables/design tokens resolved at runtime from a brand config fetched via axios + cached with @tanstack/react-query. Widgets rendered from a JSON schema (backend-driven) mapped to a component registry — no brand or copy hardcoded in components. ESLint/Prettier/Husky/commitlint wired exactly per reference configs.
Example 2:
Input: "Review this PR: a Button component hardcodes '#FF5733' and English-only text."
Output: Flag as violation of brand-driven and i18n principles. Recommend: extract color to CSS variable/design token (var(--color-brand-primary)), extract text via react-i18next (t('button.label')), and ensure component accepts props for full reusability across brands/locales. Also flag missing unicorn/no-null compliance if null is used instead of undefined.
Example 3:
Input: "Should we use Redux for server state?"
Output: No — standardize on @tanstack/react-query for all server-state (caching, revalidation, dedupe) and keep client-only state in local/component state or a minimal store. Redux (or similar) only justified for complex, cross-cutting client state unrelated to server data.
- TypeScript is non-negotiable: no
.jssource files, noanywithout explicit justification comment. - Dynamic/backend-driven first: UI structure, copy, and feature flags should be data-driven, never hardcoded, so backend/CMS changes don't require redeploys.
- Brand-driven theming: all colors, logos, fonts resolved via tokens/config, never inlined.
- Shareable & reusable: build components/hooks as headless/presentational-agnostic; push side effects to the edges (hooks, API layer).
- DX matters: lint/format/commit-hygiene enforced automatically via Husky + lint-staged + commitlint — never rely on manual discipline.
- Performance: prefer
@tanstack/react-querycaching over manual fetch/state; code-split by route/feature; memoize only when profiling justifies it. - Stability:
eqeqeq,prefer-const, strict TS, SonarJS rules (for scripts) to catch bugs/code smells early. - Naming: kebab-case or snake_case filenames only (enforced by
unicorn/filename-case). - Imports: no unused imports/vars (auto-fixed), no duplicate imports except type-only.
- Don't skip the optional React deps check —
eslint-plugin-react-hooksand@types/reactare React-only, don't add to non-React (e.g., Node/CLI) projects. - Don't hardcode brand colors, logos, or copy directly in components — breaks white-labeling/i18n.
- Don't use
nulldirectly — preferundefinedperunicorn/no-null(function arguments are exempted). - Don't bypass commit hooks (
--no-verify) — commit message format and lint-staged checks exist to protect CI. - Don't introduce alternative formatters/linters (e.g., Biome) that conflict with the established Prettier+ESLint flat-config pipeline.
- Don't manage server state with local
useState/useEffectfetch patterns — always route through@tanstack/react-query. - Don't allow
console.log— onlytable,info,error,warnare permitted by lint rules.