Everybody knows this feeling when you have to set up a new project over and over again. With new project come same decisions: framework, folder structure, validation, testing, code quality tooling and so on.
This whole process can be really frustrating so I decided to improve it by creating a frontend project starter which consists of practices that will benefit your project.
Below I'll explain which libraries I use, folder structure, AI rules and will answer the qestion “why?”.
I decided to add two options here which are Vite and Next.js for more flexibility.
I think that Next.JS is great but also it is pointless to use a framework in which you won’t utilize full range of features that it gives you. For that reason I’ve added a simpler Vite configuration.
With my templates I decided to lean into developer experience + code quality. So that’s why I have preinstalled shadcn UI library, fallow-js, zod etc.
Here is the list of most important libraries:
fallow-js- code quality control, it scans your project for dead code, code complexity, duplications and generates a detailed report that shows the issues that should be fixed.
vitest / playwright - testing libraries, both e2e and unit focused. Good project is the one with predictable code and tests is the best way to achieve this effect.
zod - data validation, sometimes TypeScript alone isn’t enough because it checks the types only at compile time while zod validates runtime data and that makes business logic integration much easier.
tanstack-query - probably the best library for server-state / async-state management which provides lots of useful things such as persistence, invalidation, caching, deduplication, retries, background refetching, etc.
shadcn/ui - it is everywhere that’s right, but I find it the best library which you can implement into project and start developing simple and attractive UI fast, it is not meant to be used for artistic projects by any stretch.
The idea of this folder structure is to provide clear responsibilities to each part of the project and minimize the chances of creating spaghetti code whether it is a feature or a UI component.
src/app/ or src/pages/ — root directories for pages / subpages.
src/features/[domain]/[FeatureName]/ — orchestration / composition layer. Here we compose presentational components, call hooks and / or hold a server action. Types and constants sit next to the component (FeatureName.types.ts, FeatureName.constants.ts) and are omitted when there is nothing to put in them.
src/components/[domain]/ — UI only folder. No fetch, no TanStack Query, no orchestration. Domain components stay here until a second domain needs them.
src/components/shared/ — the same kind of UI, promoted only after two or more domains use it. Moving it earlier creates a junk drawer of “maybe reusable” pieces.
src/components/ui/ — generated shadcn primitives (button, dialog, input). Product UI stays out, because the next shadcn add should be able to touch this folder without colliding with your screens.
src/fetchers/[domain]/ — one request per file, validated with Zod (getExample.ts next to exampleSchema.ts). TypeScript types describe the code you wrote. Zod checks the payload you actually received.
src/queries/[domain]/ — TanStack Query hooks such as useExampleQuery. They call a fetcher and own caching, invalidation, and loading state. UI imports these instead of building query options inline, so every screen that needs the same data shares one definition.
src/hooks/[domain]/ — reusable React hooks that are not data fetching: local UI state, media queries, form helpers. They stay separate from queries/ so a file named useSomething is either a Query hook or a plain hook, and you can tell which from the folder.
src/providers/AppProviders/ — Query client, theme, and other app-wide context, mounted once from layout.tsx or main.tsx.
src/utils/[domain]/ — pure functions with no React and no I/O. The Vitest file sits beside the helper (formatLabel.test.ts), because a transform is easiest to trust when its test is one file away.
src/constants/ and src/types/ — values and types shared across features. Anything used by a single component stays in that component’s folder.
src/i18n/ and src/messages/ — wiring versus copy. Config and the catalog map live in i18n. The strings live in messages, so adding a locale is a JSON file, not a refactor.
e2e/[domain]/ — Playwright specs named after the path they cover. Unit tests stay next to utils and components. End-to-end tests stay here, because they need a running app rather than a single function.
I do believe that AI isn't inherently responsible for bad code. Poor constraints, weak architecture, missing verification, and bad development processes are. That’s why I created my set of rules and guidelines for AI in the project which make development process faster and easier.
AGENTS.md — the map an agent reads first. It names the stack and points at the rules, skills, and reviewers, because the full standards are too long to repeat in every session.
.cursor/rules/core/ — constraints that are on for every file. They lock the stack and require request → spec → critique → plan → implementation → verification for non-trivial work, because an agent should not invent a second framework or skip the spec.
.cursor/rules/frontend/, testing/, tooling/ — the same standards, split by the files they touch. IDE loads a rule only when the matching files are in play, so a test edit does not carry the styling guide, and a styling edit does not carry the test guide.
.cursor/skills/spec-driven-development/ — the procedure for a real change: inspect the repo, write docs/specs/<task>.md with testable acceptance criteria, get it challenged, then implement against that file. The skill runs the workflow. The rules only forbid skipping it.
.cursor/skills/frontend-design-quality/ — the procedure for meaningful UI. It sets a visual direction and design acceptance criteria before the screen is built, then sends the rendered result to a design review.
.cursor/skills/acceptance-verification/ — the procedure after the code exists. It maps each must-pass criterion to evidence and returns pass or fail. The implementing agent does not grade its own work.
.cursor/agents/ — four read-only reviewers. spec-critic and architecture-critic run before coding. design-critic runs after a UI renders. qa-verifier runs before the task is called done. They report findings and do not edit the implementation, so the author of the change is not also the judge of it.
docs/specs/ — the written contract for each non-trivial task. Code follows the spec. If implementation uncovers a new requirement, the spec is updated before that behavior is coded.
So that’s all the approaches I use when starting a new project or maintaining one. I hope that you will find this post useful.
Also here is the link to GitHub repo - https://github.com/ddebixx/frontend-template