CommunityProgramación y desarrollogithub.com

kartikk-k/skills

Build and maintain Next.js web applications using a consistent src folder structure, domain controllers, Zod API contracts, composed components, and explicit client/server boundaries. Use for project architecture and application coding, while honoring the user's chosen libraries and providers.

¿Qué es skills?

skills is a Claude Code agent skill that build and maintain Next.js web applications using a consistent src folder structure, domain controllers, Zod API contracts, composed components, and explicit client/server boundaries. Use for project architecture and application coding, while honoring the user's chosen libraries and providers.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/kartikk-k/skills/tree/HEAD/skills/web-app-nextjs

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Next.js web app coding conventions

Apply these conventions to the requested work. Explicit user instructions override any rule or default in this skill. Preserve existing product requirements and integrations; applying this skill does not authorize an unrelated migration or installing every optional dependency.

Before implementation, inspect project instructions, installed versions, and existing conventions. Read relevant installed Next.js documentation when available, especially node_modules/next/dist/docs/; otherwise consult the version-appropriate official documentation. Do not assume framework APIs from another version.

Defaults and conditional features

AreaDefaultOverride or activation rule
FrameworkNext.js App Router and strict TypeScriptHonor the project's chosen versions and explicit user choices.
ComponentsBase UI with Tailwind CSS, wrapped in local componentsSupport Radix, shadcn/ui, or another requested library. Preserve the same component organization.
Variantsclass-variance-authority and a Tailwind-aware class-merging helperReuse an established equivalent.
API transportAxios behind custom APIClient and ServerAPIClient abstractionsAn existing custom client, fetch, or another requested transport is valid. Keep contracts independent of transport.
ValidationZod schemas with inferred TypeScript typesRequired by default at external data boundaries; change only on explicit user direction.
Remote client dataTanStack Query with a shared query wrapperReuse a requested equivalent.
Shared interactive stateZustandUse local React state for state owned by one component. Honor explicit alternatives.
AuthenticationProvider-neutral email OTP flow when authentication is requestedFollow the selected provider and requested login methods; do not choose or provision a provider implicitly.
Cross-tab synchronizationEnabled for relevant shared mutation eventsOptional and removable on request. Add events only where cross-tab consistency is useful.
Logging and tracingFollow an existing logging systemIf none exists, do not introduce, propose, or mention logging/trace infrastructure unless requested.
Mock data and simulationOffAdd only when the user requests mock data or development/testing simulation.
Development toolingSeparate development-only modulesAdd tools only when relevant or requested; no prescribed tool or overlay.

Choose defaults when a decision is needed; do not ask about each default. Do not introduce auth, state stores, synchronization, or infrastructure into features that do not need them.

Required folder structure

Use this organization for new code. Create directories as their responsibilities become necessary rather than filling the project with empty placeholders. In an existing project, avoid moving unrelated files without a migration request.

src/
├── app/                    Routes, layouts, API endpoints
├── components/
│   ├── ui/                 Shared UI primitives
│   ├── standalone/         Reusable domain components
│   ├── pages/<page>/       Page-specific components
│   └── <feature>/          Feature subsystems
├── controllers/
│   ├── client/<domain>/    Browser API calls
│   └── server/<domain>/    Server-side API calls
├── providers/              React context providers
├── stores/                 Zustand stores
├── hooks/                  Shared React hooks
├── helpers/                Auth/access and other workflows
├── lib/                    API clients, cache, logging, sync
├── db/                     Database/Redis clients
├── utils/                  Small utilities and constants
└── assets/icons/           Source SVGs

Folder names for infrastructure describe placement, not required services. No database, Redis, cache, or logging stack is implied by this tree.

  • Organize domain code beneath these responsibility folders; avoid replacing them with a different top-level architecture.
  • Use PascalCase component files, useSomething.ts hooks (.tsx only when JSX is present), and camelCase operation files such as updateName.ts.
  • Use descriptive domain and feature directories. Keep framework filenames such as page.tsx, layout.tsx, and route.ts unchanged.
  • Use index.ts to assemble controller namespaces and index.tsx as the entry point of a multi-file component.
  • Use @/ for imports rooted in src; use relative imports for close internal siblings. Use import type for type-only dependencies.
  • Avoid temporary copies, backup folders, duplicate implementations, and catch-all files with unclear responsibilities.

Routes and component composition

Keep app/ focused on routing, layout composition, request handling, and initial data loading. Use route groups such as (auth), (no-auth), and (settings) when their layout boundaries are useful. A route-group name alone provides no access protection.

Keep pages thin: load initial data, resolve page-level decisions, and render components from components/pages/<page>/ or the relevant feature directory. Server Components are the default; introduce client boundaries for browser state, events, and APIs.

Component placement:

  • components/ui/: domain-independent primitives and wrappers around the chosen UI library.
  • components/standalone/: reusable domain controls used across pages or features.
  • components/pages/<page>/: components specific to one page.
  • components/<feature>/: larger subsystems shared across routes, such as navigation or a command interface.

Split complex components into focused files. This is a core convention. A substantial component should have a directory with an index.tsx composition entry point and named supporting components, tabs, rows, hooks, and types as needed. Do not accumulate multiple substantial subcomponents and unrelated effects in one file. Split by responsibility rather than an arbitrary line limit; avoid fragmenting trivial markup.

Keep interaction mechanics in feature-local hooks or focused handler components: keyboard navigation, selection, event subscriptions, drag behavior, and view coordination. Move a hook to src/hooks/ when it is shared. Keep input shortcuts scoped so they do not interfere with editable controls; clean up listeners on unmount.

Wrap the primitive library locally and expose composable parts such as Dialog, DialogTrigger, DialogContent, and DialogTitle. Preserve accessible semantics, focus behavior, and the actual library's composition API. Do not copy Radix-specific props into Base UI wrappers without checking compatibility.

Type wrapper props from the underlying primitive or native element and add only needed custom props. Use typed variants for size, emphasis, and state. Merge consumer classes predictably and preserve ref support according to the installed React/library APIs.

Use semantic CSS variables connected to Tailwind utilities. Keep theme values centralized and component variants local. This skill prescribes styling organization, not a visual theme, spacing scale, or product design.

Compose relevant context providers in app/providers.tsx; keep their implementations in providers/. Keep browser-only providers behind an explicit client boundary and avoid mixing development tool initialization with normal provider responsibilities.

Controllers and HTTP clients

Use one operation per file under controllers/client/<domain>/ or controllers/server/<domain>/. Assemble nested namespace objects through index.ts files, for example:

ClientController.org.members.list()
ClientController.profile.updateName(input)
ServerController.org.info()

Controllers provide typed operations and endpoint construction. Components should call controllers rather than assembling URLs or transport configuration. Controllers remain thin; put reusable workflow logic in helpers/ and infrastructure in lib/.

Keep browser and server HTTP clients separate:

  • lib/APIClient.ts: browser-safe transport, query/body serialization, response handling, and cancellation support.
  • lib/ServerAPIClient.ts: server-only transport, trusted base URL, and request-scoped credentials/session context.
  • Keep server secrets and server-only dependencies out of browser imports. Never keep per-user credentials in a shared mutable singleton.
  • When server controllers call application endpoints, forward only the required credentials to trusted destinations. Do not trust a caller-supplied identity header as authentication.
  • Normalize transport errors, non-2xx responses, and application errors consistently. Preserve meaningful status/code information. Do not turn failed requests into successful empty data.
  • Support AbortSignal through controller and transport layers. Cancellation should not trigger error notifications or retries.

API schemas, contracts, and handlers

Zod schemas are the default source of truth for inputs. Define specific schemas for body, query, and path parameters, including formats, allowed values, length/range limits, optionality, and deliberate coercion. Validate before using external values. Avoid z.any(), broad records, casts, and non-null assertions as substitutes for contracts. Treat unknown keys deliberately for each boundary.

Colocate method-specific schemas and exported request/query/response types with the route by default. Infer request types with z.infer; define explicit response contracts and use response schemas where runtime validation is needed. Reuse these types in controllers and components instead of re-declaring them. If runtime contracts must be shared with browser code, or route-module export constraints require it, use an adjacent schema.ts or types.ts and type-only imports so server implementation is never pulled into the client bundle.

Use a consistent discriminated response envelope:

type APIResponse<T> =
  | { success: true; data: T; error: null }
  | {
      success: false;
      data: null;
      error: { code: string; message: string; details?: unknown };
    };

Narrow details to explicit safe validation/error detail types where used. Keep errors and HTTP statuses consistent across all branches. Do not expose internal exceptions or stack traces in production responses.

Organize multi-method routes into method-specific schema/type sections and functions such as get_request and post_request. Export handlers through withAPIRequestHandler() in lib/api/APIRequestHandler.ts.

Maintain lib/api/error/ with an APIError class and a domain-organized, typed catalog of error codes, messages, and statuses. The shared handler maps validation failures, domain errors, and unexpected failures to the same response contract. Integrate logging only under the logging condition above.

Extract repeated organization lookup, identity resolution, query parsing, and access workflows into helpers/. Keep small pure transformations and constants in utils/. Split larger integrations into focused workflow files; the route should orchestrate these steps rather than contain the entire integration.

Data fetching and state

Load initial page data through server controllers when appropriate and pass typed, serializable props into interactive components. Use Promise.all for independent requests; preserve ordering for dependent operations.

Use TanStack Query for remote client data. Provide shared defaults and a useApiQuery wrapper that unwraps the API envelope, propagates errors, and forwards the query cancellation signal. Query keys must include relevant identity/resource/filter inputs. Scope server-side query clients per request; invalidate or update affected query entries after mutations.

Use feature-specific Zustand stores for shared interactive state such as selection, filters, panels, and command state. Subscribe to individual slices or use shallow selection for grouped values. Keep setters/actions explicit and reset state when its owning resource changes. Avoid maintaining a second independent copy of remote data in Zustand when Query already owns it.

Use local state for component-owned inputs, dirty state, validation messages, and saving state. Prevent duplicate submissions and preserve user input on failures. There is no required form library.

Cancel superseded searches/filter requests and abort on cleanup. Guard against stale completions updating a new resource. Only classify actual cancellation as cancellation; do not silently swallow network failures.

For large lists, apply virtualization and pagination when useful, with loading, empty, error, retry, and end-of-list states. Reset pagination and selection appropriately when the resource/filter changes. Keep keyboard navigation consistent with virtualized rows.

Optional cross-tab synchronization

Enable synchronization for relevant shared mutations by default unless the user disables it. Do not add it to isolated state without a synchronization need.

Use a typed event registry in lib/local-sync/, domain event schemas beneath events/, and a shared hooks/useLocalSync.ts hook. Infer payload types from Zod and validate both outgoing and incoming messages.

A storage-event implementation should notify other tabs and same-tab listeners, use transient event payloads rather than persistent application state, and clean up listeners. An existing equivalent transport is acceptable. Prevent echo loops and duplicate handling; do not synchronize secrets. Events should update or invalidate the canonical state owner. Cross-tab events are UI coordination, not authorization or guaranteed delivery.

Authentication and authorization

Apply this section when auth is in scope. Keep the implementation provider-neutral and adapt to the selected service or custom backend.

  • For email OTP authentication, separate send-code, verify-code, and sign-out handlers. Define precise Zod schemas for each request and explicit success/error contracts. Respect the selected backend's token format and session lifecycle.
  • Expose client session state, loading state, and refresh behavior through providers/SessionProvider.tsx and a typed session hook. Subscribe and unsubscribe to session changes through an adapter appropriate to the backend.
  • Separate authentication (verified identity/session) from authorization (permission to act on a resource).
  • Keep shared request checks in proxy helpers when supported by the installed framework, and enforce access at the server operation boundary. UI hiding, route groups, and session context are not access controls.
  • Centralize organization/membership/resource access helpers. Scope resource queries to the verified user's permitted organization or resources; cover each applicable HTTP method.
  • Centralize typed role/permission constants. Use the roles needed by the product rather than imposing a fixed role model.
  • Keep protected layouts and API checks active. Never bypass authentication as a shortcut for development or mock data.

Errors and conditional supporting systems

Use route-level error.tsx/global-error.tsx and reusable component error boundaries where local recovery is useful. Provide meaningful retry or recovery behavior. Use existing error reporting only when present or requested; do not claim errors were reported unless reporting actually occurred.

Logging condition: when a logging system already exists, use its abstraction and conventions. When the user requests a new system, place shared infrastructure in lib/ and configure the chosen provider. Otherwise omit logging/tracing setup and suggestions from implementation and delivery. Never include credentials or sensitive payloads in logs.

Mock condition: only when explicitly requested, colocate fixtures such as mock.json beside the relevant API route or feature. Keep fixtures consistent with real contracts and gate mock execution explicitly to the intended development/test mode. Avoid unconditional early mock returns or changes to real auth enforcement.

Development tooling: place development UI in components/dev/ and other development-only integrations in src/dev/ or purpose-specific scripts. Keep these separate from product components and workflows and gate execution to development. The skill does not require a particular inspector, overlay, or tool.

Completion check

Verify the touched feature follows the folder structure, complex components are split by responsibility, contracts remain specific, and client/server dependencies stay separated. Check relevant loading/error/cancellation paths and authorization behavior when affected. Run the project's appropriate checks for the change. Do not introduce unrequested optional systems or refactor unrelated features to satisfy this checklist.

Skills relacionados