1# Universal Instructions for React / Next.js Projects
2
3> Purpose: General rules for developing various projects with React + TypeScript, Next.js + TypeScript, and Tailwind CSS.
4> Usage: Place this file in the root of a new project as `AGENTS.md`, `CLAUDE.md`, or `PROJECT_RULES.md`, or use it as a base instruction set for an AI agent.
5> Important: These instructions do not contain product-specific rules. Keep everything related to an individual project in a separate `PROJECT_RULES.md` file.
6
7---
8
9# 1. Core Principle
10
11Build a production-ready application, not a collection of disconnected components.
12
13Always follow this sequence:
14
151. Review the current project structure, `package.json`, routing, UI primitives, stores, hooks, schemas, and project rules.
162. Find existing actions, helpers, schemas, and components that can be reused.
173. Identify the smallest change required for the task.
184. Preserve existing behavior.
195. Implement each new feature end to end: model, validation, UI, storage/import/export, edge cases, and verification.
206. Run the relevant checks and report the results honestly.
21
22Do not add dependencies, abstractions, a global store, or an architectural layer unless they are genuinely necessary.
23Use `shadcn/ui` by default for UI work. Do not add another UI kit on top of it without a clear reason.
24
25---
26
27# 2. Choosing Between React and Next.js
28
29Use Next.js when the project needs:
30
31- routing;
32- SEO;
33- SSR / Server Components;
34- Server Actions;
35- Route Handlers / API routes;
36- authentication;
37- database access;
38- private environment variables;
39- content publishing.
40
41Use React + Vite when:
42
43- the application is entirely client-side;
44- SEO is not required;
45- it is a local tool, dashboard, editor, admin panel, or desktop-like UI;
46- the server already exists as a separate service.
47
48Do not choose Next.js simply because it is popular. Do not add Redux, Zustand, React Query, a form library, or another UI kit without a specific reason.
49
50---
51
52# 3. Default Stack and Checks
53
54Use the following by default:
55
56- React;
57- TypeScript in strict mode;
58- Tailwind CSS;
59- `shadcn/ui` as the required UI approach for clean design and rapid interface development;
60- Lucide React or the icon library used by the current shadcn configuration;
61- ESLint;
62- a shared `cn()` helper;
63- runtime validation for external data;
64- accessible HTML elements.
65
66Use `shadcn/ui` as the primary source of UI primitives: buttons, inputs, selects, dialogs, sheets, dropdowns, tooltips, tabs, carousels, cards, badges, skeletons, scroll areas, and other required components. Create custom primitives only when shadcn does not provide a suitable component or when the project already has a stable local primitive.
67
68For an MVP, begin with mock/JSON/localStorage data and validate local user flows first. Add the backend, database, payments, authentication, and external integrations last, once the UI, models, and flows are clear.
69
70At a minimum, run these commands after code changes:
71
72```bash
73npm run typecheck
74npm run lint
75npm run build
76```
77
78Do not claim that the project works if these commands were not run or completed with errors.
79
80---
81
82# 4. Architecture
83
84For Next.js projects expected to grow, keep source code inside `src/` by default: `src/app`, `src/components`, `src/lib`, `src/data`, `src/hooks`, and `src/features`. Keep root-level support folders and files (`public`, configuration files, lockfiles, and README) in the project root.
85
86For small projects, the following structure is acceptable:
87
88```text
89src/
90 app/ or pages/
91 components/
92 features/
93 lib/
94 shared/
95```
96
97For medium and large projects, use an FSD-like approach:
98
99```text
100src/
101 app/ # bootstrap, providers, layouts, routes
102 views/ # page-level composition
103 widgets/ # large UI blocks
104 features/ # user workflows
105 entities/ # domain model
106 shared/ # generic helpers, config, thin wrappers around shadcn/ui
107```
108
109Import direction:
110
111```text
112app/views -> widgets -> features -> entities -> shared
113```
114
115Do not:
116
117- import `widgets` into `features`;
118- place business logic in `shared`;
119- turn `shared/lib` into a dumping ground for unrelated functions;
120- duplicate mutation logic across multiple UI components;
121- use deep imports into another module's internals when that module exposes a public API.
122
123---
124
125# 5. Public API
126
127Every feature, entity, or shared UI folder should expose a clear public API through `index.ts` when the module is used externally. For shadcn primitives, the public API usually already lives in `components/ui/*` or the project's local UI layer.
128
129Good:
130
131```ts
132import { createTask } from "@/features/create-task";
133```
134
135Bad:
136
137```ts
138import { createTask } from "@/features/create-task/model/createTask";
139```
140
141Exception: internal code within the same feature or entity.
142
143---
144
145# 6. TypeScript
146
147Required:
148
149- enable `strict: true`;
150- do not use `any` except in isolated interoperability code;
151- do not hide type errors with `as` assertions;
152- use discriminated unions for complex state;
153- validate runtime JSON with a schema;
154- do not create multiple identical types without a meaningful reason.
155
156Example state type:
157
158```ts
159type LoadState<T> =
160 | { status: "idle" }
161 | { status: "loading" }
162 | { status: "success"; data: T }
163 | { status: "error"; message: string };
164```
165
166---
167
168# 7. React State and Effects
169
170Store state where it actually belongs:
171
172| State type | Where to store it |
173| ------------ | ----------------------------------------------------------- |
174| Local UI | `useState`, `useReducer` |
175| URL state | route/search parameters |
176| Server state | server rendering or a cache/query layer |
177| Form state | form hook/library |
178| Global UI | a small store when necessary |
179| Domain state | entity/store when the state is shared across multiple flows |
180
181Do not put the following in a global store:
182
183- hover state;
184- the state of a single dropdown;
185- the draft value of a single input;
186- the state of a single modal;
187- the temporary selected tab of one component.
188
189Use `useEffect` to synchronize with external systems:
190
191- browser APIs;
192- timers;
193- subscriptions;
194- external stores;
195- DOM integrations.
196
197Do not use `useEffect` for derived values.
198
199Bad:
200
201```tsx
202const [fullName, setFullName] = useState("");
203
204useEffect(() => {
205 setFullName(` `);
206}, [firstName, lastName]);
207```
208
209Good:
210
211```tsx
212const fullName = ` `;
213```
214
215---
216
217# 8. Next.js Boundaries
218
219In the App Router, components are Server Components by default.
220
221Add `"use client"` only where you need:
222
223- event handlers;
224- local state;
225- effects;
226- `window`, `document`, or `localStorage`;
227- drag and drop;
228- `contenteditable`;
229- client-only libraries.
230
231Do not make an entire layout a Client Component without a clear need.
232
233Server-only code includes:
234
235- database access;
236- authentication;
237- private API clients;
238- secret environment variables;
239- webhooks;
240- access checks.
241
242Never import a server-only module into a Client Component.
243
244---
245
246# 9. Runtime Validation and Migrations
247
248Validate all external data at the boundary:
249
250- request bodies;
251- form data;
252- URL/search parameters;
253- uploaded files;
254- imported JSON;
255- localStorage/IndexedDB data;
256- responses from external APIs.
257
258When adding a new model field, update the entire lifecycle:
259
2601. TypeScript type.
2612. Runtime schema.
2623. Factory/default values.
2634. Parser/migration for legacy data.
2645. Normalization helpers.
2656. Import/export.
2667. Search/filter indexing, if the field should be searchable.
2678. Undo/redo snapshots, if users can edit the field.
2689. UI for creating, editing, and clearing the field.
26910. Edge cases and checks.
270
271Example:
272
273```ts
274return {
275 ...item,
276 status: item.status ?? "active",
277 tags: normalizeTags(item.tags),
278 dueDate: normalizeDate(item.dueDate),
279};
280```
281
282Do not add a model field only in the UI.
283
284---
285
286# 10. Forms
287
288Every form must include:
289
290- a validation schema;
291- field errors;
292- a submitting/loading state;
293- a disabled submit button while submitting;
294- protection against duplicate submissions;
295- an error state;
296- success behavior;
297- reset/draft behavior, when applicable.
298
299A form is not complete if it works only when the request succeeds perfectly.
300
301---
302
303# 11. shadcn/ui and Shared UI
304
305Use `shadcn/ui` by default to build clean, consistent interfaces quickly.
306
307Rules:
308
309- first check whether the required component exists in the shadcn registry;
310- add shadcn components through the CLI or the project's established local method;
311- do not create a custom Button, Input, Modal, Dropdown, Tooltip, Tabs, or Card if shadcn already covers the use case;
312- adapt shadcn components through `className`, variants, and composition instead of copying similar components;
313- keep business components separate from primitives: `components/marketplace`, `features/*/ui`, `widgets/*`, or `entities/*/ui`;
314- keep only shadcn primitives and thin reusable wrappers in `components/ui` or `shared/ui`;
315- do not place product-specific business components there;
316- if shadcn does not provide a component, create a minimal local wrapper consistent with the current shadcn configuration.
317
318Base set of shadcn components for productivity interfaces:
319
320```text
321button
322input
323select
324textarea
325checkbox
326switch
327dialog
328sheet
329dropdown-menu
330popover
331tooltip
332tabs
333card
334badge
335avatar
336separator
337scroll-area
338skeleton
339carousel
340accordion
341collapsible
342hover-card
343```
344
345For marketplace, chat, and support flows, also plan for these newer shadcn components:
346
347```text
348message
349message-scroller
350attachment
351marker
352```
353
354Always use `cn()`:
355
356```ts
357export function cn(...values: Array<string | false | null | undefined>) {
358 return values.filter(Boolean).join(" ");
359}
360```
361
362---
363
364# 12. Choosing the Right UI Surface
365
366Before adding a new tool, choose the right surface:
367
368| Feature size | Placement | Example |
369| ---------------------------------- | --------------------------------- | ----------------------------------- |
370| 1-5 quick settings | context menu / dropdown / popover | status, due date, tags |
371| 5-12 grouped settings | sectioned, scrollable popover | entity properties, compact filters |
372| large data sets or bulk actions | sidebar / drawer | filters, tools panel |
373| complex form or dangerous action | modal | import/export, delete confirmation |
374| permanent workspace | dedicated view/page/widget | dashboard, calendar, editor |
375
376Rule:
377
378> If a control is used occasionally, keep it in a menu.
379> If a control is used constantly, keep it visible on the main surface.
380> If a control is complex and lengthy, move it to a sidebar or modal.
381
382Do not turn a small group of controls into a large card on the page. In productivity interfaces, this wastes valuable space.
383
384---
385
386# 13. Compact UI for Editors, Dashboards, and Workspaces
387
388In productivity applications, the primary content must remain the focus.
389
390Required:
391
392- the title, body, board, or editor must not be pushed downward by secondary controls;
393- entity properties should generally open from an icon button next to the title;
394- settings buttons must have an `aria-label`;
395- an important status can be shown as a small badge;
396- create/add actions must appear in a clear context;
397- sidebar-heavy flows must include a mobile-friendly menu or switcher;
398- do not make a productivity tool look like a landing page.
399
400Bad:
401
402```tsx
403
404 <Select>Status</Select>
405 <Select>Task</Select>
406 <Input>Date</Input>
407 <Input>Tags</Input>
408</LargePropertiesCard>
409```
410
411Good:
412
413```tsx
414
415 <TitleInput />
416 <PropertiesMenu />
417</TitleRow>
418```
419
420---
421
422# 14. Overlays, Dropdowns, Popovers, and Context Menus
423
424Every menu must behave as a true overlay.
425
426Rules:
427
428- if a menu may extend beyond its container, render it through `createPortal(..., document.body)`;
429- use `position: fixed` or a reliable positioning helper;
430- set an explicit `z-index`;
431- use an opaque `backgroundColor`;
432- do not rely only on a translucent `bg-black/50` background or blur;
433- add a border, ring, or shadow;
434- set `max-height` and `overflow-y-auto`;
435- close on `Escape`;
436- close on outside click/tap;
437- prevent page text from showing through or rendering over the menu;
438- hover and active states must not change the item's dimensions.
439
440Minimal overlay style:
441
442```tsx
443<div
444 role="menu"
445 className="rounded-2xl border p-2 shadow-2xl"
446 style="#151a21",
447 boxShadow: "0 24px 70px rgb(0 0 0 / 78%)",
448 isolation: "isolate",
449 zIndex: 1000,
450>
451 ...
452</div>
453```
454
455If the menu background does not render correctly or content appears above it, check:
456
457- the portal;
458- `position`;
459- `z-index`;
460- parent stacking contexts;
461- `isolation`;
462- opacity/background;
463- parent overflow/clipping.
464
465---
466
467# 15. Option Lists in Menus
468
469A list of tasks, projects, users, tags, or other options in a menu must not look like a dense wall of text.
470
471For a two-line item:
472
473- use a `min-height` of 40-44px;
474- include a `gap` between the icon, text, and checkmark;
475- use vertical padding such as `py-1.5`;
476- give the title and metadata different line heights;
477- add `mt-0.5` between the title and metadata;
478- apply `min-w-0` to the parent containing the text;
479- apply `truncate` to the title and metadata;
480- apply `shrink-0` to checkmarks and icons.
481
482Example:
483
484```tsx
485<button className="flex min-h-11 items-center gap-2.5 rounded-lg px-2.5 py-1.5">
486 <span className="min-w-0 flex-1">
487 <span className="block truncate font-medium leading-5"></span>
488 <span className="mt-0.5 block truncate text-xs leading-4 text-muted">
489 {meta}
490 </span>
491 </span>
492 {isActive ? <Check className="shrink-0" /> : null}
493</button>
494```
495
496---
497
498# 16. Long Text and Overflow
499
500Any user-provided text may contain a long word with no spaces.
501
502For editors, `contenteditable` elements, Markdown, card titles, and comments:
503
504- use `min-w-0` on flex/grid children;
505- use the current Tailwind utilities for wrapping long words;
506- in newer Tailwind versions, `break-words` may be written as `wrap-break-word`;
507- check the documentation for the project's current Tailwind version before using wrapping, overflow, text-wrap, grid, spacing, or arbitrary-value classes;
508- if an element is inside a flex container and long text breaks its width, check whether `wrap-anywhere` is appropriate;
509- use `truncate` for short lines in cards;
510- wrap body text instead of allowing horizontal overflow;
511- text must not render over a menu, popover, or modal;
512- test with a long string containing no spaces.
513
514For an editable block:
515
516```tsx
517className = "min-w-0 wrap-break-word whitespace-pre-wrap";
518```
519
520If the project uses an older Tailwind version where `wrap-break-word` is unavailable, check the installed Tailwind version and the official documentation or version notes, then use a supported equivalent: `break-words`, an arbitrary value, or a CSS property.
521
522For a badge:
523
524```tsx
525className = "inline-flex whitespace-nowrap";
526```
527
528A badge must not compress text vertically. If it does not fit, move it to a new line or use `truncate` with an explicit, understandable width.
529
530---
531
532# 17. Tailwind CSS: Verify Current Class Names
533
534The AI agent must check the Tailwind version installed in the project before using new or potentially version-dependent classes.
535
536Process:
537
5381. Inspect `package.json` and the lockfile.
5392. Determine the Tailwind major version.
5403. If a class may differ between versions, check the official documentation for that exact version.
5414. Do not replace classes mechanically without verification.
5425. When using an arbitrary value, confirm that it is included in the build output.
543
544Pay particular attention to:
545
546- `break-words` / `wrap-break-word` / `wrap-anywhere`;
547- `text-wrap`, `text-balance`, and `text-pretty`;
548- `overflow-*`;
549- `size-*`;
550- arbitrary colors such as `bg-[#151a21]`;
551- arbitrary shadows;
552- arbitrary grid templates;
553- dynamic class names.
554
555Do not build dynamic Tailwind classes like this:
556
557```tsx
558const color = "red";
559return <div className={`bg--500`} />;
560```
561
562Tailwind may not detect that class during the build. Use a map:
563
564```tsx
565const colorClassName = {
566 danger: "bg-red-500",
567 success: "bg-emerald-500",
568};
569```
570
571If an important overlay background must not depend on Tailwind's build output, using an inline `style.backgroundColor` is acceptable.
572
573---
574
575# 18. Layout and Sidebar Collapse
576
577Collapsing a sidebar or drawer must not change the page height or leave an empty block.
578
579Rules:
580
581- app shell: `h-dvh min-h-dvh overflow-hidden`;
582- internal regions: `flex min-h-0 flex-1 overflow-hidden`;
583- enable scrolling only on the appropriate region with `overflow-y-auto`;
584- when collapsing, change width/flex-basis rather than height;
585- a collapsed sidebar must have a stable width;
586- provide a clear control for restoring the sidebar;
587- destructive or creation actions must not remain as isolated buttons without context;
588- preferences may be persisted in localStorage.
589
590Example:
591
592```tsx
593<main className="flex h-dvh min-h-dvh flex-col overflow-hidden">
594 <div className="flex min-h-0 flex-1 overflow-hidden">
595 <Sidebar className="h-full min-h-0 shrink-0" />
596 <section className="min-h-0 flex-1 overflow-y-auto" />
597 </div>
598</main>
599```
600
601---
602
603# 19. Browser APIs and localStorage
604
605In Next.js, browser APIs are available only in Client Components.
606
607Rules:
608
609- a file that uses `localStorage`, `window`, `document`, drag and drop, or `contenteditable` must include `"use client"`;
610- do not read `localStorage` in a Server Component;
611- do not cause hydration errors with different initial values;
612- wrap storage operations in `try/catch`;
613- storage failures must not break the UI;
614- verify persisted UI preferences after a reload;
615- the build must not fail with `window is not defined`.
616
617Example:
618
619```tsx
620const toggle = useCallback(() => {
621 setIsCollapsed((current) => {
622 const next = !current;
623
624 try {
625 window.localStorage.setItem(KEY, next ? "true" : "false");
626 } catch {
627 // UI still works without browser storage.
628 }
629
630 return next;
631 });
632}, []);
633```
634
635Verify that:
636
637- the default state works with empty storage;
638- a reload preserves the state;
639- private mode or storage errors do not break the screen;
640- the build does not fail with `window is not defined`.
641
642---
643
644# 20. Relationships Between Tools
645
646If one entity is linked to another, the relationship must be real:
647
648- store it in the model;
649- show it in the UI;
650- clicking it opens the linked entity;
651- when creating the related entity, save the relationship immediately;
652- preserve the relationship during import/export;
653- include the relationship in search/filter behavior when useful;
654- if the related entity is deleted, show a fallback in the UI.
655
656Do not create a decorative "Link" button if the relationship is not persisted.
657
658---
659
660# 21. Unified Domain Operations
661
662Each user operation must have a single source of truth.
663
664Do not:
665
666- create an entity one way from the slash menu;
667- create it another way from the toolbar;
668- bypass validation from the command palette;
669- duplicate mutation logic in the context menu.
670
671Instead:
672
673- keep the domain operation in one place;
674- have UI components call that operation;
675- use the same validation and constraints for every entry point.
676
677---
678
679# 23. Accessibility
680
681Required:
682
683- use `<button>` for actions;
684- use `<a>` for navigation;
685- add `aria-label` to icon-only buttons;
686- provide labels for inputs;
687- show a visible focus state;
688- support keyboard navigation;
689- close modals and popovers on `Escape`;
690- close popovers on outside click;
691- use a focus trap in modals;
692- do not use color as the only way to communicate meaning;
693- do not replace `<button>` with ``.
694
695---
696
697# 24. Loading, Empty, and Error States
698
699Data-driven screens must account for:
700
701- loading;
702- success;
703- empty state;
704- permission denied;
705- network error;
706- server error;
707- retry.
708
709A blank screen with no explanation is a bug.
710
711---
712
713# 25. Security
714
715Required:
716
717- keep secrets on the server only;
718- use runtime validation;
719- enforce access control on the server;
720- validate file MIME types and sizes;
721- sanitize user-provided HTML;
722- do not use `dangerouslySetInnerHTML` without a sanitizer;
723- do not log tokens or personal data;
724- do not trust `role` or `userId` values supplied by the browser.
725
726---
727
728# 26. Performance
729
730Measure first, then optimize.
731
732Use:
733
734- dynamic imports for heavy editor, chart, map, and PDF modules;
735- image optimization;
736- virtualization for large lists;
737- abort/stale-request protection for search;
738- selectors to reduce rerenders.
739
740Do not add memoization without a reason.
741
742---
743
744# 27. Test the Design with Realistic Content
745
746For additional guidance on interface quality, you may refer to:
747
748- https://jakub.kr/skills/make-interfaces-feel-better
749
750This resource is useful when polishing typography, hover states, shadows, borders, spacing, optical alignment, micro-interactions, and the overall feel of the interface.
751
752Before completing a UI task, test it with:
753
754- a long word with no spaces;
755- a long Russian title;
756- a short title;
757- an empty title;
758- multiple tags;
759- a long list/category name;
760- multiple options in a dropdown;
761- active and inactive statuses;
762- a date and a missing date.
763
764Verify that:
765
766- nothing overlaps;
767- overlays cover the underlying content;
768- text does not show through menus;
769- badges do not compress text vertically;
770- elements do not crowd each other;
771- scrollbars do not cover important text;
772- hover and focus states are easy to read;
773- desktop and mobile widths both look correct.
774
775---
776
777# 28. Checks After Changes
778
779After code changes, run:
780
781```bash
782npm run typecheck
783npm run lint
784npm run build
785```
786
787If the UI was changed:
788
789- open the page in a browser;
790- complete the primary user flow;
791- test keyboard and mouse interaction;
792- test `Escape` and outside-click behavior;
793- test reloading;
794- test long text;
795- test a mobile viewport width;
796- take a screenshot if the visual layer changed.
797
798If browser verification is impossible, say so explicitly. Do not present `typecheck` as visual verification.
799
800---
801
802# 29. Git and the Working Tree
803
804Before making changes, inspect the current state:
805
806```bash
807git status --short
808```
809
810Rules:
811
812- do not revert someone else's changes without an explicit request;
813- do not use destructive commands without explicit permission;
814- do not perform unrelated refactoring;
815- do not commit automatically unless the user asks you to;
816- do not change line endings or reformat the entire project unnecessarily.
817
818---
819
820# 30. Final Report
821
822In the final response, state:
823
824- what changed;
825- which files are important;
826- which checks were run;
827- what could not be verified;
828- which risks remain.
829
830Keep the report concise and honest.
831