
Software Frontend
- 190 installs
- 73 repo stars
- Updated July 13, 2026
- vasilyu1983/ai-agents-public
Helps with frontend development tasks.
About
software-frontend is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted development.
- software-frontend
- Frontend Development
- AI-coding skill
Software Frontend by the numbers
- 190 all-time installs (skills.sh)
- +10 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #868 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vasilyu1983/ai-agents-public --skill software-frontendAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 190 |
|---|---|
| repo stars | ★ 73 |
| Last updated | July 13, 2026 |
| Repository | vasilyu1983/ai-agents-public ↗ |
What it does
Helps with frontend development tasks.
Files
Frontend Engineering
Production-ready patterns for modern web applications.
Stack (March 2026): Next.js 16 + Turbopack, React 19.x + Server Components, TypeScript 5.9+ (strict), Tailwind CSS v4, TanStack Query, Zustand, Vitest (browser mode).
Breaking Changes: Next.js 16 Upgrade Guide
Shared release gates: ../software-clean-code-standard/assets/checklists/frontend-performance-a11y-checklist.md
If you use React Server Components (RSC), treat security advisories as blocking: see data/sources.json (React RSC advisories).
Quick Reference
| Task | Tool | Command |
|---|---|---|
| Next.js App | Next.js 16 + Turbopack | npx create-next-app@latest |
| Vue App | Nuxt 4 | npx nuxi@latest init |
| Angular App | Angular 21 | ng new |
| Svelte App | SvelteKit 2.49+ | npm create svelte@latest |
| React SPA | Vite + React | npm create vite@latest |
| UI Components | shadcn/ui | npx shadcn@latest init |
Workflow
1. Pick a framework using the decision tree. 2. Start from a matching template in assets/. 3. Implement feature-specific patterns from references/. 4. Treat accessibility and performance as release gates (shared checklist above).
Framework Decision Tree
Project needs:
|-- React ecosystem?
| |-- Full-stack + SEO -> Next.js 16
| |-- Progressive enhancement -> Remix
| `-- Client-side SPA -> Vite + React
|
|-- Vue ecosystem?
| |-- Full-stack -> Nuxt 4
| `-- SPA -> Vite + Vue 3.5+
|
|-- State management?
| |-- Server data -> TanStack Query
| |-- Global client -> Zustand
| `-- WARNING: DECLINING: Redux
|
`-- Styling?
|-- Utility-first -> Tailwind CSS v4
`-- WARNING: DECLINING: CSS-in-JSNext.js 16 Key Changes
proxy.ts replaces middleware.ts
npx @next/codemod@canary upgrade latest # recommended
mv middleware.ts proxy.ts # or manual renameCache Components ("use cache")
export default async function Page() {
"use cache";
const data = await fetchData();
return <ProductList data={data} />;
}React Compiler
// next.config.ts
const nextConfig: NextConfig = {
experimental: { reactCompiler: true },
};For the full migration checklist (async APIs, image config, parallel routes, caching APIs), see references/operational-playbook.md (Next.js 16 Migration Checklist section).
Performance Budgets
| Metric | Target |
|---|---|
| LCP | <= 2.5s |
| INP | <= 200ms |
| CLS | <= 0.1 |
| TTFB | < 600ms |
Operational Discipline
Verification Order
1. Lint edited files only. 2. Type-check edited feature surface. 3. Run full project lint/type/build once before handoff.
Avoid repeated full builds while known local lint/type failures remain.
Watch For
- Lint pitfalls:
react-hooks/set-state-in-effect,react-hooks/purity,react-hooks/rules-of-hooks,react/no-unescaped-entities - CLI drift: Run
npx eslint --help/npx vitest --helpbefore assuming flags from older setups - Route deletion: After deleting/renaming any page, grep for stale imports and
<Link>hrefs - Architecture pre-check: Before adding new context providers or state stores, search for existing patterns first
- Hydration mismatches: Use
useState(null) + useEffectfor browser-only values — seereferences/production-gotchas.md - macOS Turbopack: Set
ulimit -n 10240to avoid EMFILE errors in large projects
Handoff Requirements
Include in final output: exact files changed, lint/type/build commands run, whether failures are new or baseline, one prevention note for any repeated class of issue.
Deployment Checklist
Pre-Deployment
- [ ]
npm run build— no errors - [ ]
npm run lint— zero ESLint errors - [ ]
vitest run— all tests passing - [ ] Bundle size within budget
- [ ] Environment variables set
Accessibility
- [ ] axe DevTools — zero critical issues
- [ ] Keyboard navigation works
- [ ] Color contrast >= 4.5:1
- [ ] Screen reader tested
SEO
- [ ] Metadata configured
- [ ] sitemap.xml generated
- [ ] robots.txt configured
Reference Routing
Read only the reference matching the user's framework or problem — not all of them.
| User's topic | Read this |
|---|---|
| Next.js, RSC, Server Actions, data fetching | references/fullstack-patterns.md (see section index below) |
| Next.js migration, upgrade, breaking changes | references/operational-playbook.md |
| Hydration bugs, storage access, response parsing | references/production-gotchas.md |
| Vue 3, Nuxt 4, Pinia, composables | references/vue-nuxt-patterns.md |
| Angular, signals, standalone components | references/angular-patterns.md |
| Svelte 5, SvelteKit, runes | references/svelte-sveltekit-patterns.md |
| Remix, loaders, actions, progressive enhancement | references/remix-react-patterns.md |
| Vite + React SPA (no Next.js / no SSR) | references/vite-react-patterns.md |
| State management (Zustand, TanStack Query, Redux) | references/state-management-patterns.md |
| Testing (Vitest, Testing Library, Playwright, MSW) | references/testing-frontend-patterns.md |
| Lighthouse, bundle size, Core Web Vitals | references/performance-optimization.md |
| Quick HTML prototype / artifact | references/artifacts-builder.md |
fullstack-patterns.md Section Index
This file is 2044 lines. Read only the section you need:
| Section | Lines | When to read |
|---|---|---|
| Authentication (JWT, Zustand auth store) | 27–497 | Auth flow, protected routes, login forms |
| Blog Posts CRUD (Prisma, API routes, forms) | 499–1264 | CRUD features, list/detail pages, create forms |
| Real-time data with Server Components | 1266–1355 | Direct DB access in RSC, streaming |
| Server Actions for mutations | 1357–1627 | Form submissions, "use server", revalidation |
| tRPC end-to-end type safety | 1629–2020 | tRPC setup, type-safe API clients |
| Key patterns summary | 1992–2044 | Quick reference for type sharing, validation |
Templates
| Framework | Template |
|---|---|
| Next.js | assets/nextjs/template-nextjs-tailwind-shadcn.md |
| Vue/Nuxt | assets/vue-nuxt/template-nuxt4-tailwind.md |
| Angular | assets/angular/template-angular21-standalone.md |
| Svelte | assets/svelte/template-sveltekit-runes.md |
| Vite+React | assets/vite-react/template-vite-react-ts.md |
| Remix | assets/remix/template-remix-react.md |
Related Skills
| Skill | Purpose |
|---|---|
| software-backend | Backend API |
| dev-api-design | REST/GraphQL |
| software-code-review | Code review |
| ops-devops-platform | CI/CD |
Fact-Checking
Use web search to verify current external facts, versions, and platform behavior before final answers. Prefer primary sources; report source links and dates for volatile information.
Angular 21 Standalone Components Starter Template
Production-ready template for building modern Angular applications with standalone components, zoneless change detection, signals, and TypeScript.
---
Overview
This template provides a modern Angular 21 setup with:
- Angular 21 - Latest Angular with zoneless change detection, esbuild, and standalone components
- Signals - Angular's fine-grained reactivity system
- TypeScript - Strict type safety
- Standalone Components - No NgModules required
- Angular Material or PrimeNG - UI component libraries
- TailwindCSS (optional) - Utility-first styling
- RxJS - Reactive programming
- Jasmine + Karma or Jest - Unit testing
- Cypress or Playwright - E2E testing
---
Project Setup
Initialize Project
# Install Angular CLI
npm install -g @angular/cli@latest
# Create new project (standalone components by default)
ng new my-app --routing --style=scss --standalone
cd my-app
# Add Angular Material
ng add @angular/material
# OR add PrimeNG
npm install primeng primeicons
# Add TailwindCSS (optional)
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
# Add testing
# Jest (alternative to Jasmine)
ng add @briebug/jest-schematic
# Playwright for E2E
npm init playwright@latest---
Project Structure
my-app/
|-- src/
| |-- app/
| | |-- app.component.ts # Root component
| | |-- app.config.ts # Application configuration
| | |-- app.routes.ts # Route configuration
| | |-- core/ # Core services (singleton)
| | | |-- services/
| | | | |-- auth.service.ts
| | | | `-- api.service.ts
| | | |-- guards/
| | | | `-- auth.guard.ts
| | | `-- interceptors/
| | | `-- auth.interceptor.ts
| | |-- shared/ # Shared components/utilities
| | | |-- components/
| | | | |-- button/
| | | | | |-- button.component.ts
| | | | | |-- button.component.html
| | | | | |-- button.component.scss
| | | | | `-- button.component.spec.ts
| | | | `-- header/
| | | |-- directives/
| | | |-- pipes/
| | | `-- models/
| | |-- features/ # Feature modules
| | | |-- auth/
| | | | |-- login/
| | | | | |-- login.component.ts
| | | | | `-- login.component.html
| | | | `-- register/
| | | |-- dashboard/
| | | `-- blog/
| | | |-- blog-list/
| | | |-- blog-detail/
| | | `-- blog.service.ts
| | `-- layout/ # Layout components
| | |-- main-layout/
| | `-- auth-layout/
| |-- assets/ # Static files
| |-- environments/ # Environment configs
| | |-- environment.ts
| | `-- environment.prod.ts
| |-- styles.scss # Global styles
| |-- index.html
| `-- main.ts # Bootstrap file
|-- angular.json # Angular CLI config
|-- tsconfig.json # TypeScript config
|-- tailwind.config.js # Tailwind config (if used)
`-- package.json---
Configuration Files
main.ts (Bootstrap)
// src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { AppComponent } from './app/app.component';
import { routes } from './app/app.routes';
import { authInterceptor } from './app/core/interceptors/auth.interceptor';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
provideAnimations(),
],
}).catch((err) => console.error(err));app.config.ts
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { routes } from './app.routes';
import { authInterceptor } from './core/interceptors/auth.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor])),
provideAnimations(),
],
};app.routes.ts
// src/app/app.routes.ts
import { Routes } from '@angular/router';
import { authGuard } from './core/guards/auth.guard';
export const routes: Routes = [
{
path: '',
loadComponent: () =>
import('./features/home/home.component').then((m) => m.HomeComponent),
},
{
path: 'login',
loadComponent: () =>
import('./features/auth/login/login.component').then(
(m) => m.LoginComponent
),
},
{
path: 'dashboard',
loadComponent: () =>
import('./features/dashboard/dashboard.component').then(
(m) => m.DashboardComponent
),
canActivate: [authGuard],
},
{
path: 'blog',
loadChildren: () =>
import('./features/blog/blog.routes').then((m) => m.BLOG_ROUTES),
},
{
path: '**',
loadComponent: () =>
import('./shared/components/not-found/not-found.component').then(
(m) => m.NotFoundComponent
),
},
];tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"moduleResolution": "node",
"importHelpers": true,
"target": "ES2022",
"module": "ES2022",
"useDefineForClassFields": false,
"lib": ["ES2022", "dom"],
"paths": {
"@app/*": ["src/app/*"],
"@core/*": ["src/app/core/*"],
"@shared/*": ["src/app/shared/*"],
"@features/*": ["src/app/features/*"]
}
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
}
}---
Common Patterns
Standalone Component with Signals
// src/app/features/counter/counter.component.ts
import { Component, signal, computed } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-counter',
standalone: true,
imports: [CommonModule],
template: `
<div class="counter">
<h2>Counter: {{ count() }}</h2>
<p>Double: {{ doubleCount() }}</p>
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
<button (click)="reset()">Reset</button>
</div>
`,
styles: [`
.counter {
text-align: center;
padding: 2rem;
}
button {
margin: 0 0.5rem;
padding: 0.5rem 1rem;
}
`],
})
export class CounterComponent {
// Signal for reactive state
count = signal(0);
// Computed signal (derived state)
doubleCount = computed(() => this.count() * 2);
increment() {
this.count.update(value => value + 1);
}
decrement() {
this.count.update(value => value - 1);
}
reset() {
this.count.set(0);
}
}Component with HTTP Service
// src/app/features/blog/blog-list/blog-list.component.ts
import { Component, OnInit, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BlogService } from '../blog.service';
import { Blog } from '@shared/models/blog.model';
@Component({
selector: 'app-blog-list',
standalone: true,
imports: [CommonModule],
template: `
<div class="blog-list">
<h1>Blog Posts</h1>
@if (loading()) {
<p>Loading...</p>
}
@if (error()) {
<p class="error">{{ error() }}</p>
}
@if (blogs().length > 0) {
<div class="grid">
@for (blog of blogs(); track blog.id) {
<article class="blog-card">
<h2>{{ blog.title }}</h2>
<p>{{ blog.excerpt }}</p>
<a [routerLink]="['/blog', blog.id]">Read more</a>
</article>
}
</div>
}
</div>
`,
styles: [`
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}
.blog-card {
padding: 1.5rem;
border: 1px solid #ddd;
border-radius: 8px;
}
`],
})
export class BlogListComponent implements OnInit {
blogs = signal<Blog[]>([]);
loading = signal(false);
error = signal<string | null>(null);
constructor(private blogService: BlogService) {}
ngOnInit() {
this.loadBlogs();
}
loadBlogs() {
this.loading.set(true);
this.blogService.getBlogs().subscribe({
next: (data) => {
this.blogs.set(data);
this.loading.set(false);
},
error: (err) => {
this.error.set('Failed to load blogs');
this.loading.set(false);
},
});
}
}Service with Signals
// src/app/core/services/auth.service.ts
import { Injectable, signal, computed } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Router } from '@angular/router';
import { Observable, tap } from 'rxjs';
import { User } from '@shared/models/user.model';
@Injectable({
providedIn: 'root',
})
export class AuthService {
private readonly API_URL = 'https://api.example.com';
// Signals for reactive state
private userSignal = signal<User | null>(null);
private tokenSignal = signal<string | null>(null);
// Computed signals
user = this.userSignal.asReadonly();
isAuthenticated = computed(() => !!this.userSignal());
constructor(
private http: HttpClient,
private router: Router
) {
this.loadFromStorage();
}
login(credentials: { email: string; password: string }): Observable<any> {
return this.http.post(`${this.API_URL}/auth/login`, credentials).pipe(
tap((response: any) => {
this.userSignal.set(response.user);
this.tokenSignal.set(response.token);
localStorage.setItem('token', response.token);
})
);
}
logout() {
this.userSignal.set(null);
this.tokenSignal.set(null);
localStorage.removeItem('token');
this.router.navigate(['/login']);
}
private loadFromStorage() {
const token = localStorage.getItem('token');
if (token) {
this.tokenSignal.set(token);
this.fetchCurrentUser().subscribe();
}
}
private fetchCurrentUser(): Observable<User> {
return this.http.get<User>(`${this.API_URL}/auth/me`).pipe(
tap((user) => this.userSignal.set(user))
);
}
}Route Guard (Functional)
// src/app/core/guards/auth.guard.ts
import { inject } from '@angular/core';
import { Router, CanActivateFn } from '@angular/router';
import { AuthService } from '@core/services/auth.service';
export const authGuard: CanActivateFn = (route, state) => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isAuthenticated()) {
return true;
}
// Redirect to login
return router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url },
});
};HTTP Interceptor (Functional)
// src/app/core/interceptors/auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '@core/services/auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const token = localStorage.getItem('token');
if (token) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
return next(req);
};Reactive Form with Validation
// src/app/features/auth/login/login.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '@core/services/auth.service';
@Component({
selector: 'app-login',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<div class="login-container">
<h1>Login</h1>
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="email">Email</label>
<input
id="email"
type="email"
formControlName="email"
[class.error]="email?.invalid && email?.touched"
/>
@if (email?.invalid && email?.touched) {
<span class="error-message">
@if (email?.errors?.['required']) {
Email is required
}
@if (email?.errors?.['email']) {
Invalid email format
}
</span>
}
</div>
<div class="form-group">
<label for="password">Password</label>
<input
id="password"
type="password"
formControlName="password"
[class.error]="password?.invalid && password?.touched"
/>
@if (password?.invalid && password?.touched) {
<span class="error-message">
Password must be at least 8 characters
</span>
}
</div>
<button type="submit" [disabled]="loginForm.invalid || submitting">
{{ submitting ? 'Logging in...' : 'Login' }}
</button>
@if (error) {
<p class="error">{{ error }}</p>
}
</form>
</div>
`,
styles: [`
.form-group {
margin-bottom: 1rem;
}
input.error {
border-color: red;
}
.error-message {
color: red;
font-size: 0.875rem;
}
`],
})
export class LoginComponent {
loginForm = this.fb.nonNullable.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
});
submitting = false;
error: string | null = null;
// Getters for form controls
get email() {
return this.loginForm.get('email');
}
get password() {
return this.loginForm.get('password');
}
constructor(
private fb: FormBuilder,
private authService: AuthService,
private router: Router
) {}
onSubmit() {
if (this.loginForm.invalid) return;
this.submitting = true;
this.error = null;
this.authService.login(this.loginForm.getRawValue()).subscribe({
next: () => {
this.submitting = false;
this.router.navigate(['/dashboard']);
},
error: (err) => {
this.submitting = false;
this.error = 'Login failed. Please check your credentials.';
},
});
}
}Custom Pipe
// src/app/shared/pipes/date-ago.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'dateAgo',
standalone: true,
})
export class DateAgoPipe implements PipeTransform {
transform(value: Date | string): string {
const date = new Date(value);
const now = new Date();
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
if (seconds < 60) return 'just now';
if (seconds < 3600) return `${Math.floor(seconds / 60)} minutes ago`;
if (seconds < 86400) return `${Math.floor(seconds / 3600)} hours ago`;
if (seconds < 604800) return `${Math.floor(seconds / 86400)} days ago`;
return date.toLocaleDateString();
}
}---
Testing
Component Test (Jasmine)
// src/app/features/counter/counter.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CounterComponent } from './counter.component';
describe('CounterComponent', () => {
let component: CounterComponent;
let fixture: ComponentFixture<CounterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CounterComponent],
}).compileComponents();
fixture = TestBed.createComponent(CounterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should increment count', () => {
component.increment();
expect(component.count()).toBe(1);
});
it('should decrement count', () => {
component.decrement();
expect(component.count()).toBe(-1);
});
it('should reset count', () => {
component.count.set(5);
component.reset();
expect(component.count()).toBe(0);
});
it('should compute double count', () => {
component.count.set(5);
expect(component.doubleCount()).toBe(10);
});
});Service Test
// src/app/core/services/auth.service.spec.ts
import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AuthService],
});
service = TestBed.inject(AuthService);
httpMock = TestBed.inject(HttpTestingController);
});
afterEach(() => {
httpMock.verify();
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should login and set user', () => {
const mockResponse = {
user: { id: 1, email: 'test@example.com' },
token: 'fake-token',
};
service.login({ email: 'test@example.com', password: 'password' }).subscribe();
const req = httpMock.expectOne('https://api.example.com/auth/login');
expect(req.request.method).toBe('POST');
req.flush(mockResponse);
expect(service.user()).toEqual(mockResponse.user);
expect(service.isAuthenticated()).toBe(true);
});
});---
Production Checklist
Performance
- [ ] Enable production mode in
environment.prod.ts - [ ] Use
OnPushchange detection for performance-critical components - [ ] Lazy load feature modules with
loadChildren - [ ] Optimize bundle size with tree-shaking
- [ ] Use trackBy in
@forloops
Build Configuration
- [ ] Run
ng build --configuration production - [ ] Enable AOT compilation (enabled by default)
- [ ] Enable build optimization
- [ ] Configure budget limits in
angular.json
Security
- [ ] Sanitize user input to prevent XSS
- [ ] Use HTTP interceptors for auth tokens
- [ ] Implement CSRF protection
- [ ] Configure Content Security Policy
- [ ] Use environment variables for sensitive data
Testing
- [ ] Maintain 80%+ test coverage
- [ ] Run E2E tests before deployment
- [ ] Test accessibility with axe-core
---
Useful Commands
# Development
ng serve # Start dev server (localhost:4200)
ng serve --open # Start and open browser
# Generate
ng generate component my-component --standalone
ng generate service my-service
ng generate guard my-guard --functional
ng generate pipe my-pipe --standalone
# Build
ng build # Development build
ng build --configuration production # Production build
# Testing
ng test # Run unit tests
ng e2e # Run E2E tests
# Linting
ng lint # Run ESLint---
Additional Resources
---
Notes
- Standalone components are the default in Angular 21
- Zoneless change detection is the default in Angular 21 - no more Zone.js overhead
- Esbuild is the default bundler - faster builds than Webpack
- Signals provide fine-grained reactivity - use for local state
- Use `@if`, `@for`, `@switch` instead of
*ngIf,*ngFor,*ngSwitch - Functional guards and interceptors are the modern approach
- Lazy loading with `loadComponent` for better performance
Frontend Engineering - Next.js 16 + Tailwind CSS + shadcn/ui Template
Purpose: Production-grade web applications with Next.js 16 App Router, TypeScript, Tailwind CSS, and shadcn/ui components.
---
When to Use
Use this template when building:
- Modern web applications with SSR/SSG
- Full-stack applications (with API routes)
- Dashboard and admin interfaces
- SaaS products
- E-commerce platforms
- Marketing sites with dynamic content
- Progressive Web Apps (PWAs)
---
TEMPLATE STARTS HERE
1. Project Overview
Project Name: [Name]
Description: [Brief description]
Tech Stack:
- [ ] Next.js 16.x (App Router, Turbopack default, proxy.ts)
- [ ] React 19.2.x
- [ ] TypeScript 5.9.x
- [ ] Tailwind CSS v4.x
- [ ] shadcn/ui (Radix UI primitives)
- [ ] Prisma 6.x (optional - if full-stack)
- [ ] PostgreSQL (optional - if full-stack)
Team:
- Owner: [Name]
- Frontend Lead: [Name]
- Designer: [Name]
Timeline:
- Start: [YYYY-MM-DD]
- MVP: [YYYY-MM-DD]
- Launch: [YYYY-MM-DD]
---
2. Project Setup
2.1 Initial Setup
# Create Next.js app with TypeScript and Tailwind
npx create-next-app@latest my-app --typescript --tailwind --app --eslint
cd my-app
# Install shadcn/ui
npx shadcn-ui@latest init
# Install additional dependencies
npm install zod react-hook-form @hookform/resolvers/zod
npm install zustand
npm install swr
npm install framer-motion
npm install date-fns
npm install lucide-react
npm install class-variance-authority clsx tailwind-merge
# Dev dependencies
npm install -D @types/node
npm install -D prettier prettier-plugin-tailwindcss
npm install -D vitest @testing-library/react @testing-library/jest-dom
npm install -D @playwright/test2.2 Project Structure
my-app/
|-- app/
| |-- (auth)/
| | |-- login/
| | | `-- page.tsx
| | `-- register/
| | `-- page.tsx
| |-- (dashboard)/
| | |-- layout.tsx
| | |-- page.tsx
| | |-- users/
| | | |-- page.tsx
| | | `-- [id]/
| | | `-- page.tsx
| | `-- settings/
| | `-- page.tsx
| |-- api/
| | |-- auth/
| | | `-- route.ts
| | `-- users/
| | |-- route.ts
| | `-- [id]/
| | `-- route.ts
| |-- layout.tsx
| |-- page.tsx
| |-- loading.tsx
| |-- error.tsx
| `-- not-found.tsx
|-- src/
| |-- components/
| | |-- ui/ # shadcn/ui components
| | | |-- button.tsx
| | | |-- input.tsx
| | | |-- dialog.tsx
| | | `-- ...
| | |-- forms/
| | | |-- login-form.tsx
| | | `-- user-form.tsx
| | |-- layouts/
| | | |-- header.tsx
| | | |-- footer.tsx
| | | `-- sidebar.tsx
| | `-- shared/
| | |-- loading.tsx
| | `-- error-boundary.tsx
| |-- lib/
| | |-- utils.ts
| | |-- cn.ts
| | |-- api.ts
| | |-- auth.ts
| | |-- store.ts
| | `-- hooks/
| | |-- use-auth.ts
| | `-- use-toast.ts
| |-- types/
| | `-- index.ts
| |-- styles/
| | `-- globals.css
| `-- providers/
| |-- auth-provider.tsx
| `-- theme-provider.tsx
|-- public/
| |-- images/
| `-- fonts/
|-- prisma/ # If using database
| `-- schema.prisma
|-- tests/
| |-- unit/
| |-- integration/
| `-- e2e/
|-- .env.example
|-- .env.local
|-- .eslintrc.json
|-- .prettierrc
|-- next.config.js
|-- tailwind.config.ts
|-- tsconfig.json
|-- components.json # shadcn/ui config
|-- vitest.config.ts
`-- playwright.config.ts---
3. Configuration Files
3.1 tailwind.config.ts
import type { Config } from 'tailwindcss';
const config: Config = {
darkMode: ['class'],
content: [
'./pages/**/*.{ts,tsx}',
'./components/**/*.{ts,tsx}',
'./app/**/*.{ts,tsx}',
'./src/**/*.{ts,tsx}',
],
theme: {
container: {
center: true,
padding: '2rem',
screens: {
'2xl': '1400px',
},
},
extend: {
colors: {
border: 'hsl(var(--border))',
input: 'hsl(var(--input))',
ring: 'hsl(var(--ring))',
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
secondary: {
DEFAULT: 'hsl(var(--secondary))',
foreground: 'hsl(var(--secondary-foreground))',
},
destructive: {
DEFAULT: 'hsl(var(--destructive))',
foreground: 'hsl(var(--destructive-foreground))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
accent: {
DEFAULT: 'hsl(var(--accent))',
foreground: 'hsl(var(--accent-foreground))',
},
popover: {
DEFAULT: 'hsl(var(--popover))',
foreground: 'hsl(var(--popover-foreground))',
},
card: {
DEFAULT: 'hsl(var(--card))',
foreground: 'hsl(var(--card-foreground))',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
keyframes: {
'accordion-down': {
from: { height: '0' },
to: { height: 'var(--radix-accordion-content-height)' },
},
'accordion-up': {
from: { height: 'var(--radix-accordion-content-height)' },
to: { height: '0' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
'accordion-up': 'accordion-up 0.2s ease-out',
},
},
},
plugins: [require('tailwindcss-animate')],
};
export default config;3.2 next.config.ts (Next.js 16)
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
images: {
// 'domains' is deprecated - use remotePatterns instead
remotePatterns: [
{
protocol: 'https',
hostname: 'your-domain.com',
},
],
formats: ['image/avif', 'image/webp'],
},
// Turbopack is now default - remove --turbopack flags from scripts
turbopack: {
// Turbopack options (previously experimental.turbopack)
},
// If using proxy.ts and need URL normalization disabled
// skipProxyUrlNormalize: true,
};
export default nextConfig;3.3 proxy.ts (Next.js 16 - replaces middleware.ts)
// proxy.ts (root of project)
// NOTE: Runs on Node.js runtime (not Edge). Use middleware.ts if you need Edge.
import { NextRequest, NextResponse } from 'next/server';
export function proxy(request: NextRequest) {
// Authentication check example
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add custom headers
const response = NextResponse.next();
response.headers.set('x-custom-header', 'value');
return response;
}
export const config = {
matcher: [
// Match all paths except static files and api routes
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};3.4 .env.example
# App
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Database (if using Prisma)
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
# Auth (if using NextAuth)
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-change-in-production
# APIs (if needed)
NEXT_PUBLIC_API_URL=http://localhost:3000/api---
4. Root Layout
4.1 app/layout.tsx
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { Providers } from '@/providers';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: {
default: 'My App',
template: '%s | My App',
},
description: 'My Next.js application',
keywords: ['next.js', 'react', 'typescript'],
authors: [{ name: 'Your Name' }],
openGraph: {
type: 'website',
locale: 'en_US',
url: 'https://your-domain.com',
siteName: 'My App',
},
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<Providers>
{children}
</Providers>
</body>
</html>
);
}4.2 src/providers/index.tsx
'use client';
import { ThemeProvider } from './theme-provider';
import { AuthProvider } from './auth-provider';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<AuthProvider>
{children}
</AuthProvider>
</ThemeProvider>
);
}---
5. Common Components
5.1 Loading States
// components/shared/loading.tsx
import { Loader2 } from 'lucide-react';
export function LoadingSpinner() {
return (
<div className="flex items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
</div>
);
}
export function LoadingSkeleton() {
return (
<div className="space-y-4">
<div className="h-12 w-full animate-pulse rounded-lg bg-muted" />
<div className="h-12 w-full animate-pulse rounded-lg bg-muted" />
<div className="h-12 w-full animate-pulse rounded-lg bg-muted" />
</div>
);
}
// app/loading.tsx
import { LoadingSpinner } from '@/components/shared/loading';
export default function Loading() {
return (
<div className="flex min-h-screen items-center justify-center">
<LoadingSpinner />
</div>
);
}5.2 Error Handling
// app/error.tsx
'use client';
import { useEffect } from 'react';
import { Button } from '@/components/ui/button';
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
useEffect(() => {
console.error(error);
}, [error]);
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<h2 className="mb-4 text-2xl font-bold">Something went wrong!</h2>
<p className="mb-4 text-muted-foreground">{error.message}</p>
<Button onClick={reset}>Try again</Button>
</div>
);
}5.3 Not Found
// app/not-found.tsx
import Link from 'next/link';
import { Button } from '@/components/ui/button';
export default function NotFound() {
return (
<div className="flex min-h-screen flex-col items-center justify-center">
<h1 className="mb-2 text-6xl font-bold">404</h1>
<h2 className="mb-4 text-2xl">Page Not Found</h2>
<p className="mb-8 text-muted-foreground">
The page you're looking for doesn't exist.
</p>
<Button asChild>
<Link href="/">Go Home</Link>
</Button>
</div>
);
}---
6. Authentication Example
6.1 Login Form Component
// components/forms/login-form.tsx
'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Button } from '@/components/ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
const loginSchema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type LoginForm = z.infer<typeof loginSchema>;
export function LoginForm() {
const router = useRouter();
const [error, setError] = useState<string | null>(null);
const form = useForm<LoginForm>({
resolver: zodResolver(loginSchema),
defaultValues: {
email: '',
password: '',
},
});
async function onSubmit(data: LoginForm) {
try {
setError(null);
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Login failed');
}
router.push('/dashboard');
router.refresh();
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
}
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
type="email"
placeholder="you@example.com"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input type="password" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{error && (
<div className="rounded-md bg-destructive/10 p-3 text-sm text-destructive">
{error}
</div>
)}
<Button
type="submit"
className="w-full"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting ? 'Logging in...' : 'Login'}
</Button>
</form>
</Form>
);
}6.2 Login Page
// app/(auth)/login/page.tsx
import { Metadata } from 'next';
import Link from 'next/link';
import { LoginForm } from '@/components/forms/login-form';
export const metadata: Metadata = {
title: 'Login',
description: 'Login to your account',
};
export default function LoginPage() {
return (
<div className="flex min-h-screen items-center justify-center px-4">
<div className="w-full max-w-md space-y-8">
<div className="text-center">
<h1 className="text-3xl font-bold">Welcome back</h1>
<p className="mt-2 text-muted-foreground">
Login to your account to continue
</p>
</div>
<div className="rounded-lg border bg-card p-8">
<LoginForm />
</div>
<p className="text-center text-sm text-muted-foreground">
Don't have an account?{' '}
<Link
href="/register"
className="font-medium text-primary hover:underline"
>
Register
</Link>
</p>
</div>
</div>
);
}---
7. Dashboard Layout
7.1 Dashboard Layout with Sidebar
// app/(dashboard)/layout.tsx
import { Header } from '@/components/layouts/header';
import { Sidebar } from '@/components/layouts/sidebar';
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex min-h-screen">
<Sidebar />
<div className="flex flex-1 flex-col">
<Header />
<main className="flex-1 p-6">{children}</main>
</div>
</div>
);
}7.2 Sidebar Component
// components/layouts/sidebar.tsx
'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { cn } from '@/lib/utils';
import { Home, Users, Settings } from 'lucide-react';
const navigation = [
{ name: 'Dashboard', href: '/dashboard', icon: Home },
{ name: 'Users', href: '/dashboard/users', icon: Users },
{ name: 'Settings', href: '/dashboard/settings', icon: Settings },
];
export function Sidebar() {
const pathname = usePathname();
return (
<div className="flex w-64 flex-col border-r bg-card">
<div className="p-6">
<h1 className="text-2xl font-bold">My App</h1>
</div>
<nav className="flex-1 space-y-1 px-3">
{navigation.map((item) => {
const isActive = pathname === item.href;
return (
<Link
key={item.name}
href={item.href}
className={cn(
'flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors',
isActive
? 'bg-primary text-primary-foreground'
: 'hover:bg-accent hover:text-accent-foreground'
)}
>
<item.icon className="h-5 w-5" />
{item.name}
</Link>
);
})}
</nav>
</div>
);
}7.3 Header Component
// components/layouts/header.tsx
'use client';
import { useAuth } from '@/lib/hooks/use-auth';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { User } from 'lucide-react';
export function Header() {
const { user, logout } = useAuth();
return (
<header className="flex h-16 items-center justify-between border-b px-6">
<div className="flex items-center gap-4">
<h2 className="text-lg font-semibold">Dashboard</h2>
</div>
<div className="flex items-center gap-4">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<User className="h-5 w-5" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>
{user?.name || 'User'}
</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={logout}>
Logout
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</header>
);
}---
8. Data Fetching Example
8.1 Server Component with Data Fetching
// app/(dashboard)/users/page.tsx
import { Suspense } from 'react';
import { LoadingSkeleton } from '@/components/shared/loading';
import { UserCard } from '@/components/users/user-card';
async function getUsers() {
const res = await fetch('https://api.example.com/users', {
next: { revalidate: 3600 }, // Revalidate every hour
});
if (!res.ok) {
throw new Error('Failed to fetch users');
}
return res.json();
}
async function UsersList() {
const users = await getUsers();
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{users.map((user: User) => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}
export default function UsersPage() {
return (
<div>
<h1 className="mb-6 text-3xl font-bold">Users</h1>
<Suspense fallback={<LoadingSkeleton />}>
<UsersList />
</Suspense>
</div>
);
}8.2 Client Component with SWR
// components/users/user-list-client.tsx
'use client';
import useSWR from 'swr';
import { UserCard } from './user-card';
import { LoadingSkeleton } from '@/components/shared/loading';
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export function UserListClient() {
const { data: users, error, isLoading } = useSWR('/api/users', fetcher);
if (error) {
return <div>Failed to load users</div>;
}
if (isLoading) {
return <LoadingSkeleton />;
}
return (
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{users.map((user: User) => (
<UserCard key={user.id} user={user} />
))}
</div>
);
}---
9. Utilities
9.1 cn() Utility
// lib/utils.ts
import { type ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}9.2 API Client
// lib/api.ts
class APIError extends Error {
constructor(public status: number, message: string) {
super(message);
}
}
export async function fetcher<T>(
url: string,
options?: RequestInit
): Promise<T> {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new APIError(response.status, error.message || 'API Error');
}
return response.json();
}---
10. Testing
10.1 Component Test
// components/ui/button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi } from 'vitest';
import { Button } from './button';
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
it('calls onClick when clicked', () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click</Button>);
fireEvent.click(screen.getByText('Click'));
expect(onClick).toHaveBeenCalledOnce();
});
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Click</Button>);
expect(screen.getByText('Click')).toBeDisabled();
});
});10.2 E2E Test
// tests/e2e/auth.spec.ts
import { test, expect } from '@playwright/test';
test('user can login', async ({ page }) => {
await page.goto('/login');
await page.fill('input[name="email"]', 'user@example.com');
await page.fill('input[name="password"]', 'password123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('text=Welcome back')).toBeVisible();
});---
11. Production Checklist
11.1 Performance
- [ ] Image optimization (Next/Image)
- [ ] Code splitting (dynamic imports)
- [ ] Bundle size analysis
- [ ] Lazy loading components
- [ ] Route prefetching
- [ ] Static generation where possible
- [ ] API response caching
11.2 SEO
- [ ] Metadata for all pages
- [ ] Open Graph tags
- [ ] Twitter cards
- [ ] Sitemap generation
- [ ] robots.txt
- [ ] Semantic HTML
11.3 Accessibility
- [ ] Keyboard navigation
- [ ] Screen reader support
- [ ] ARIA labels
- [ ] Color contrast (WCAG AA)
- [ ] Focus indicators
- [ ] Alt text for images
11.4 Security
- [ ] Environment variables secure
- [ ] CSP headers
- [ ] CORS configuration
- [ ] Input validation
- [ ] XSS prevention
- [ ] CSRF protection
11.5 Monitoring
- [ ] Error tracking (Sentry)
- [ ] Analytics (Google Analytics, Plausible)
- [ ] Performance monitoring
- [ ] User feedback system
---
END
This template provides a production-ready foundation for Next.js 16 applications. Customize based on specific project requirements.
Remix + React Starter Template
Full-stack React framework with server-side rendering, loaders, and actions.
---
Overview
- Remix - Full-stack React framework
- React 19 - Latest React
- TypeScript - Type safety
- TailwindCSS - Styling
- Prisma - Database ORM
- Vitest - Testing
---
Quick Start
npx create-remix@latest my-app
cd my-app
npm install
# Add TailwindCSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# Add Prisma
npm install -D prisma
npm install @prisma/client
npx prisma init---
Project Structure
my-app/
|-- app/
| |-- routes/
| | |-- _index.tsx # Home route (/)
| | |-- login.tsx # /login
| | |-- blog._index.tsx # /blog
| | `-- blog.$slug.tsx # /blog/:slug
| |-- components/
| |-- utils/
| |-- root.tsx # Root component
| `-- entry.client.tsx
|-- public/
`-- remix.config.js---
Core Patterns
Route with Loader
// app/routes/blog.$slug.tsx
import { json, type LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { prisma } from '~/utils/db.server';
export async function loader({ params }: LoaderFunctionArgs) {
const post = await prisma.post.findUnique({
where: { slug: params.slug },
});
if (!post) {
throw new Response('Not Found', { status: 404 });
}
return json({ post });
}
export default function BlogPost() {
const { post } = useLoaderData<typeof loader>();
return (
<article>
<h1>{post.title}</h1>
<p>{post.excerpt}</p>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}Route with Action (Form)
// app/routes/login.tsx
import { json, redirect, type ActionFunctionArgs } from '@remix-run/node';
import { Form, useActionData } from '@remix-run/react';
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const email = formData.get('email');
const password = formData.get('password');
const result = loginSchema.safeParse({ email, password });
if (!result.success) {
return json(
{ errors: result.error.flatten().fieldErrors },
{ status: 400 }
);
}
// Authenticate
const user = await authenticateUser(result.data);
if (!user) {
return json(
{ errors: { email: 'Invalid credentials' } },
{ status: 401 }
);
}
// Create session
return redirect('/dashboard', {
headers: {
'Set-Cookie': await createUserSession(user.id),
},
});
}
export default function Login() {
const actionData = useActionData<typeof action>();
return (
<Form method="post">
<div>
<label htmlFor="email">Email</label>
<input
id="email"
name="email"
type="email"
required
/>
{actionData?.errors?.email && (
<span className="error">{actionData.errors.email}</span>
)}
</div>
<div>
<label htmlFor="password">Password</label>
<input
id="password"
name="password"
type="password"
required
/>
{actionData?.errors?.password && (
<span className="error">{actionData.errors.password}</span>
)}
</div>
<button type="submit">Login</button>
</Form>
);
}Layout Route
// app/routes/_layout.tsx
import { Outlet } from '@remix-run/react';
export default function Layout() {
return (
<div className="min-h-screen flex flex-col">
<header className="bg-white shadow">
<nav className="container mx-auto px-4 py-4">
{/* Navigation */}
</nav>
</header>
<main className="flex-1">
<Outlet />
</main>
<footer className="bg-gray-100 py-8">
<div className="container mx-auto px-4 text-center">
(c) 2025 My App
</div>
</footer>
</div>
);
}---
Key Features
Loaders - Fetch data on the server Actions - Handle mutations on the server Form component - Progressive enhancement Automatic revalidation - Data stays fresh Optimistic UI - Instant feedback
---
Resources
SvelteKit + Svelte 5 Runes Starter Template
Production-ready template for building modern full-stack applications with SvelteKit and Svelte 5 runes reactivity.
---
Overview
- SvelteKit - Full-stack Svelte framework with SSR/SSG
- Svelte 5 - Latest with runes-based reactivity
- TypeScript - Type safety
- TailwindCSS - Utility-first styling
- Prisma or Drizzle - Database ORM
- Vitest - Unit testing
- Playwright - E2E testing
---
Quick Start
# Create project
npm create svelte@latest my-app
cd my-app
npm install
# Add TailwindCSS
npx svelte-add@latest tailwindcss
npm install
# Add Prisma
npm install -D prisma
npm install @prisma/client
npx prisma init
# Testing
npm install -D vitest @testing-library/svelte
npm init playwright@latest---
Project Structure
my-app/
|-- src/
| |-- lib/
| | |-- components/ # Reusable components
| | |-- server/ # Server-only code
| | `-- stores/ # Svelte stores
| |-- routes/
| | |-- +page.svelte # Home (/)
| | |-- +layout.svelte # Root layout
| | |-- blog/
| | | |-- +page.svelte # /blog
| | | `-- [slug]/
| | | |-- +page.svelte # /blog/:slug
| | | `-- +page.server.ts
| | `-- api/
| | `-- posts/
| | `-- +server.ts # API route
| |-- app.html # HTML template
| `-- app.css # Global styles
|-- static/ # Static assets
|-- svelte.config.js
|-- vite.config.ts
`-- tailwind.config.ts---
Core Patterns
Component with Runes
<!-- src/lib/components/Counter.svelte -->
<script lang="ts">
// Reactive state with $state()
let count = $state(0);
// Derived state with $derived()
let doubled = $derived(count * 2);
function increment() {
count += 1;
}
function decrement() {
count -= 1;
}
</script>
<div class="counter">
<h2>Count: {count}</h2>
<p>Doubled: {doubled}</p>
<button onclick={increment}>+</button>
<button onclick={decrement}>-</button>
</div>
<style>
.counter {
text-align: center;
padding: 2rem;
}
</style>Page with Data Loading
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
// Props with $props()
let { data }: { data: PageData } = $props();
// Reactive variables
let liked = $state(false);
</script>
<article>
<h1>{data.post.title}</h1>
<p>{data.post.excerpt}</p>
<div>{@html data.post.content}</div>
<button onclick={() => liked = !liked}>
{liked ? 'Liked' : 'Like'}
</button>
</article>// src/routes/blog/[slug]/+page.server.ts
import type { PageServerLoad } from './$types';
import { prisma } from '$lib/server/prisma';
export const load: PageServerLoad = async ({ params }) => {
const post = await prisma.post.findUnique({
where: { slug: params.slug },
});
if (!post) {
throw error(404, 'Post not found');
}
return { post };
};Form Actions
<!-- src/routes/login/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { ActionData } from './$types';
let { form }: { form: ActionData } = $props();
</script>
<form method="POST" use:enhance>
<input
name="email"
type="email"
placeholder="Email"
required
/>
{#if form?.errors?.email}
<span class="error">{form.errors.email}</span>
{/if}
<input
name="password"
type="password"
placeholder="Password"
required
/>
{#if form?.errors?.password}
<span class="error">{form.errors.password}</span>
{/if}
<button type="submit">Login</button>
{#if form?.success}
<p class="success">Login successful!</p>
{/if}
</form>// src/routes/login/+page.server.ts
import { fail, redirect } from '@sveltejs/kit';
import type { Actions } from './$types';
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
export const actions: Actions = {
default: async ({ request, cookies }) => {
const formData = await request.formData();
const email = formData.get('email');
const password = formData.get('password');
const result = loginSchema.safeParse({ email, password });
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
});
}
// Authenticate user
const user = await authenticateUser(result.data);
if (!user) {
return fail(401, {
errors: { email: 'Invalid credentials' },
});
}
// Set session
cookies.set('session', user.sessionToken, {
path: '/',
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 60 * 60 * 24 * 7, // 1 week
});
throw redirect(303, '/dashboard');
},
};API Route
// src/routes/api/posts/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { prisma } from '$lib/server/prisma';
export const GET: RequestHandler = async ({ url }) => {
const page = Number(url.searchParams.get('page')) || 1;
const limit = Number(url.searchParams.get('limit')) || 10;
const posts = await prisma.post.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: { createdAt: 'desc' },
});
const total = await prisma.post.count();
return json({
data: posts,
meta: { page, limit, total },
});
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const post = await prisma.post.create({
data: body,
});
return json(post, { status: 201 });
};Universal Reactive Store
// src/lib/stores/auth.svelte.ts
// Runes work in .ts files with .svelte.ts extension
export function createAuthStore() {
let user = $state<User | null>(null);
let token = $state<string | null>(null);
// Computed
let isAuthenticated = $derived(!!user);
return {
get user() { return user; },
get token() { return token; },
get isAuthenticated() { return isAuthenticated; },
login(newUser: User, newToken: string) {
user = newUser;
token = newToken;
},
logout() {
user = null;
token = null;
},
};
}
// Export singleton
export const authStore = createAuthStore();Usage:
<script lang="ts">
import { authStore } from '$lib/stores/auth.svelte';
</script>
<div>
{#if authStore.isAuthenticated}
<p>Welcome, {authStore.user?.name}!</p>
<button onclick={() => authStore.logout()}>Logout</button>
{:else}
<a href="/login">Login</a>
{/if}
</div>---
Testing
Component Test
// src/lib/components/Counter.test.ts
import { render, screen, fireEvent } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import Counter from './Counter.svelte';
describe('Counter', () => {
it('renders initial count', () => {
render(Counter);
expect(screen.getByText(/Count: 0/)).toBeInTheDocument();
});
it('increments count', async () => {
render(Counter);
const button = screen.getByText('+');
await fireEvent.click(button);
expect(screen.getByText(/Count: 1/)).toBeInTheDocument();
});
});---
Production Checklist
- [ ] Configure adapter for deployment (
@sveltejs/adapter-vercel,adapter-node, etc.) - [ ] Enable prerendering for static pages
- [ ] Optimize images with
enhanced:img - [ ] Set up environment variables
- [ ] Configure CSP headers
- [ ] Run
npm run buildand test
---
Commands
npm run dev # Start dev server
npm run build # Build for production
npm run preview # Preview production build
npm run test # Run tests---
Resources
Vite + React + TypeScript Starter Template
Lightning-fast React development with Vite bundler.
---
Overview
- Vite - Next-generation frontend tooling
- React 19 - Latest React with hooks
- TypeScript - Type safety
- TailwindCSS - Styling
- React Router - Client-side routing
- TanStack Query - Data fetching
- Vitest - Testing
---
Quick Start
# Create project
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
# Add TailwindCSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
# Add React Router
npm install react-router-dom
# Add TanStack Query
npm install @tanstack/react-query
# Add testing
npm install -D vitest @testing-library/react @testing-library/jest-dom---
Project Structure
my-app/
|-- src/
| |-- components/
| |-- pages/
| |-- hooks/
| |-- utils/
| |-- App.tsx
| |-- main.tsx
| `-- index.css
|-- public/
|-- index.html
|-- vite.config.ts
`-- tailwind.config.js---
Core Patterns
Component with Hooks
// src/components/Counter.tsx
import { useState } from 'react';
export function Counter() {
const [count, setCount] = useState(0);
return (
<div className="counter">
<h2>Count: {count}</h2>
<button onClick={() => setCount(count + 1)}>+</button>
<button onClick={() => setCount(count - 1)}>-</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}Data Fetching with TanStack Query
// src/pages/BlogList.tsx
import { useQuery } from '@tanstack/react-query';
interface Post {
id: number;
title: string;
excerpt: string;
}
async function fetchPosts(): Promise<Post[]> {
const res = await fetch('https://api.example.com/posts');
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
}
export function BlogList() {
const { data, isLoading, error } = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div className="grid gap-4">
{data?.map((post) => (
<article key={post.id} className="p-4 border rounded">
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}Custom Hook
// src/hooks/useAuth.ts
import { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
email: string;
}
export function useAuth() {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
fetchUser(token).then(setUser).finally(() => setLoading(false));
} else {
setLoading(false);
}
}, []);
const login = async (credentials: { email: string; password: string }) => {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials),
});
const data = await res.json();
setUser(data.user);
localStorage.setItem('token', data.token);
};
const logout = () => {
setUser(null);
localStorage.removeItem('token');
};
return {
user,
loading,
isAuthenticated: !!user,
login,
logout,
};
}---
Configuration
vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': '/src',
},
},
server: {
port: 3000,
},
});---
Commands
npm run dev # Start dev server (instant HMR)
npm run build # Build for production
npm run preview # Preview production build
npm run test # Run tests---
Resources
Nuxt 4 + Vue 3 + Tailwind CSS Starter Template
Production-ready template for building full-stack web applications with Nuxt 4, Vue 3, TypeScript, and Tailwind CSS.
---
Overview
This template provides a modern Nuxt 4 setup with:
- Nuxt 4 - Progressive Vue.js framework with Nitro server engine
- Vue 3 - Composition API with
<script setup> - TypeScript - Full type safety
- Tailwind CSS - Utility-first styling
- Pinia - Official Vue state management
- Nuxt UI or shadcn-vue - Component libraries
- VueUse - Composition utilities
- Vitest - Unit testing
- Playwright - E2E testing
---
Project Setup
Initialize Project
# Create Nuxt 4 project
npx nuxi@latest init my-app
cd my-app
# Install dependencies
npm install
# Add Tailwind CSS
npx nuxi@latest module add @nuxtjs/tailwindcss
# TypeScript support is built in (enable strict mode in `tsconfig.json`)
# Add UI components (choose one)
# Option A: Nuxt UI (recommended)
npx nuxi@latest module add @nuxt/ui
# Option B: shadcn-vue
npx nuxi@latest module add shadcn-nuxt
npx shadcn-vue@latest init
# Add state management
npm install pinia @pinia/nuxt
# Add VueUse
npm install @vueuse/nuxt @vueuse/core
# Add testing
npm install -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright---
Project Structure
my-app/
|-- .nuxt/ # Build output (auto-generated)
|-- .output/ # Production build
|-- app.vue # Root component
|-- nuxt.config.ts # Nuxt configuration
|-- tsconfig.json # TypeScript config
|-- tailwind.config.ts # Tailwind config
|-- pages/ # File-based routing
| |-- index.vue # Home page (/)
| |-- about.vue # About page (/about)
| `-- blog/
| |-- index.vue # /blog
| `-- [slug].vue # /blog/:slug (dynamic route)
|-- layouts/ # Layouts
| |-- default.vue # Default layout
| `-- dashboard.vue # Dashboard layout
|-- components/ # Auto-imported components
| |-- ui/ # UI components (shadcn-vue)
| |-- AppHeader.vue # Header component
| `-- AppFooter.vue # Footer component
|-- composables/ # Auto-imported composables
| |-- useAuth.ts # Auth composable
| `-- useApi.ts # API composable
|-- server/ # Server directory (Nitro)
| |-- api/ # API routes
| | |-- users.get.ts # GET /api/users
| | `-- users/
| | `-- [id].get.ts # GET /api/users/:id
| |-- middleware/ # Server middleware
| `-- utils/ # Server utilities
|-- stores/ # Pinia stores
| |-- auth.ts # Auth store
| `-- cart.ts # Cart store
|-- assets/ # Uncompiled assets
| `-- css/
| `-- main.css # Global CSS
|-- public/ # Static files
| |-- favicon.ico
| `-- images/
`-- tests/ # Tests
|-- unit/
`-- e2e/---
Configuration Files
nuxt.config.ts
// nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
'@nuxtjs/tailwindcss',
'@nuxt/ui', // or 'shadcn-nuxt'
'@pinia/nuxt',
'@vueuse/nuxt',
],
typescript: {
strict: true,
typeCheck: true,
},
app: {
head: {
title: 'My Nuxt App',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'description', content: 'My awesome Nuxt 4 app' },
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
],
},
},
// Runtime config (environment variables)
runtimeConfig: {
// Private keys (server-only)
apiSecret: process.env.API_SECRET,
// Public keys (exposed to client)
public: {
apiBase: process.env.API_BASE_URL || 'http://localhost:3000',
},
},
// Nitro server configuration
nitro: {
preset: 'node-server', // or 'vercel', 'netlify', etc.
},
})tailwind.config.ts
// tailwind.config.ts
import type { Config } from 'tailwindcss'
export default <Partial<Config>>{
content: [
'./components/**/*.{js,vue,ts}',
'./layouts/**/*.vue',
'./pages/**/*.vue',
'./plugins/**/*.{js,ts}',
'./app.vue',
],
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
},
},
},
plugins: [],
}---
Common Patterns
Page with Data Fetching
<!-- pages/blog/[slug].vue -->
<script setup lang="ts">
// Route params are auto-typed
const route = useRoute()
const slug = route.params.slug
// useAsyncData for data fetching (SSR-friendly)
const { data: post, error } = await useAsyncData(
`post-${slug}`,
() => $fetch(`/api/posts/${slug}`)
)
// Handle not found
if (!post.value) {
throw createError({
statusCode: 404,
message: 'Post not found',
})
}
// SEO metadata
useSeoMeta({
title: post.value.title,
description: post.value.excerpt,
ogImage: post.value.image,
})
</script>
<template>
<div class="container mx-auto px-4 py-8">
<article v-if="post">
<h1 class="text-4xl font-bold mb-4">{{ post.title }}</h1>
<p class="text-gray-600 mb-8">{{ post.excerpt }}</p>
<div v-html="post.content" />
</article>
</div>
</template>Component with Composables
<!-- components/UserProfile.vue -->
<script setup lang="ts">
// Auto-imported composables
const { user, isAuthenticated, logout } = useAuth()
const { formatDate } = useFormatters()
// Component props
interface Props {
showEmail?: boolean
}
const props = withDefaults(defineProps<Props>(), {
showEmail: false,
})
// Local state
const isEditing = ref(false)
// Computed
const displayName = computed(() =>
user.value ? `${user.value.firstName} ${user.value.lastName}` : 'Guest'
)
// Methods
const handleLogout = async () => {
await logout()
navigateTo('/login')
}
</script>
<template>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-2xl font-bold">{{ displayName }}</h2>
<button
@click="handleLogout"
class="text-red-600 hover:text-red-800"
>
Logout
</button>
</div>
<div v-if="user">
<p v-if="showEmail" class="text-gray-600">{{ user.email }}</p>
<p class="text-sm text-gray-500">
Member since {{ formatDate(user.createdAt) }}
</p>
</div>
</div>
</template>Composable (Auto-imported)
// composables/useAuth.ts
export const useAuth = () => {
const user = useState<User | null>('user', () => null)
const token = useCookie('auth_token')
const isAuthenticated = computed(() => !!user.value)
const login = async (credentials: LoginCredentials) => {
const data = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials,
})
user.value = data.user
token.value = data.token
}
const logout = async () => {
await $fetch('/api/auth/logout', { method: 'POST' })
user.value = null
token.value = null
}
const fetchUser = async () => {
if (!token.value) return
try {
user.value = await $fetch('/api/auth/me')
} catch (error) {
token.value = null
}
}
return {
user: readonly(user),
isAuthenticated,
login,
logout,
fetchUser,
}
}API Route (Server)
// server/api/posts/index.get.ts
export default defineEventHandler(async (event) => {
// Query parameters
const query = getQuery(event)
const page = Number(query.page) || 1
const limit = Number(query.limit) || 10
// Database query (example with Prisma)
const posts = await prisma.post.findMany({
skip: (page - 1) * limit,
take: limit,
orderBy: { createdAt: 'desc' },
})
const total = await prisma.post.count()
return {
data: posts,
meta: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
},
}
})API Route with Validation
// server/api/posts/index.post.ts
import { z } from 'zod'
const postSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(10),
published: z.boolean().default(false),
})
export default defineEventHandler(async (event) => {
// Parse and validate body
const body = await readBody(event)
const result = postSchema.safeParse(body)
if (!result.success) {
throw createError({
statusCode: 400,
message: 'Validation failed',
data: result.error.flatten(),
})
}
// Create post
const post = await prisma.post.create({
data: result.data,
})
return post
})Pinia Store
// stores/auth.ts
import { defineStore } from 'pinia'
export const useAuthStore = defineStore('auth', () => {
// State
const user = ref<User | null>(null)
const token = useCookie('auth_token')
// Getters
const isAuthenticated = computed(() => !!user.value)
const userName = computed(() => user.value?.name || 'Guest')
// Actions
const login = async (credentials: LoginCredentials) => {
const data = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials,
})
user.value = data.user
token.value = data.token
}
const logout = () => {
user.value = null
token.value = null
}
return {
user,
token,
isAuthenticated,
userName,
login,
logout,
}
})Layout
<!-- layouts/default.vue -->
<script setup lang="ts">
const { user } = useAuth()
</script>
<template>
<div class="min-h-screen flex flex-col">
<header class="bg-white shadow">
<nav class="container mx-auto px-4 py-4">
<div class="flex items-center justify-between">
<NuxtLink to="/" class="text-xl font-bold">
My App
</NuxtLink>
<div class="flex items-center gap-4">
<NuxtLink to="/about">About</NuxtLink>
<NuxtLink to="/blog">Blog</NuxtLink>
<div v-if="user">
<NuxtLink to="/dashboard">Dashboard</NuxtLink>
</div>
<div v-else>
<NuxtLink to="/login">Login</NuxtLink>
</div>
</div>
</div>
</nav>
</header>
<main class="flex-1">
<slot />
</main>
<footer class="bg-gray-100 py-8">
<div class="container mx-auto px-4 text-center text-gray-600">
(c) 2025 My App. All rights reserved.
</div>
</footer>
</div>
</template>---
Testing
Unit Test (Vitest)
// tests/unit/composables/useAuth.test.ts
import { describe, it, expect, beforeEach } from 'vitest'
import { useAuth } from '~/composables/useAuth'
describe('useAuth', () => {
beforeEach(() => {
// Reset state
})
it('should initialize with no user', () => {
const { user, isAuthenticated } = useAuth()
expect(user.value).toBeNull()
expect(isAuthenticated.value).toBe(false)
})
it('should login successfully', async () => {
const { login, user, isAuthenticated } = useAuth()
await login({ email: 'test@example.com', password: 'password' })
expect(user.value).toBeTruthy()
expect(isAuthenticated.value).toBe(true)
})
})E2E Test (Playwright)
// tests/e2e/auth.test.ts
import { test, expect } from '@playwright/test'
test('user can login', async ({ page }) => {
await page.goto('/')
// Click login link
await page.click('text=Login')
// Fill form
await page.fill('input[name="email"]', 'test@example.com')
await page.fill('input[name="password"]', 'password')
await page.click('button[type="submit"]')
// Verify redirect
await expect(page).toHaveURL('/dashboard')
await expect(page.locator('text=Dashboard')).toBeVisible()
})---
Production Checklist
Performance
- [ ] Enable image optimization with
<NuxtImg>component - [ ] Use
lazyloading for below-fold content - [ ] Implement route-level code splitting (automatic)
- [ ] Configure caching strategies in
nitro.config.ts - [ ] Use
<ClientOnly>for client-only components
SEO
- [ ] Configure
useSeoMeta()for all pages - [ ] Add
robots.txtin/public - [ ] Add
sitemap.xml(use@nuxtjs/sitemapmodule) - [ ] Configure Open Graph meta tags
- [ ] Enable server-side rendering (default)
Security
- [ ] Use runtime config for sensitive data
- [ ] Implement CSRF protection for API routes
- [ ] Add rate limiting to API routes
- [ ] Configure CORS properly
- [ ] Use
httpOnlycookies for auth tokens
Deployment
- [ ] Set
NODE_ENV=production - [ ] Configure environment variables
- [ ] Run
npm run buildand test.output/ - [ ] Choose deployment preset (Vercel, Netlify, Node)
- [ ] Set up CI/CD pipeline
---
Useful Commands
# Development
npm run dev # Start dev server (localhost:3000)
# Build
npm run build # Build for production
npm run preview # Preview production build locally
# Testing
npm run test # Run unit tests
npm run test:e2e # Run E2E tests
# Type checking
npm run typecheck # Check TypeScript types
# Linting
npm run lint # Run ESLint
npm run lint:fix # Fix ESLint errors---
Additional Resources
---
Notes
- Auto-imports: Components, composables, and Vue APIs are auto-imported
- TypeScript: Full type safety with strict mode enabled
- SSR by default: Pages render on the server for better SEO and performance
- API routes: Built-in server with Nitro engine
- File-based routing: Pages automatically become routes
{
"metadata": {
"skill": "software-frontend-engineering",
"updated": "2026-01-17",
"total_sources": 124,
"primary_stack": "Next.js 16 + TypeScript + Tailwind CSS + shadcn/ui",
"extensible": true,
"supported_frameworks": "Next.js, Vue/Nuxt, Angular, Svelte/SvelteKit, Remix, Vite+React",
"latest_updates": "January 2026: Added DevTools MCP, Cache Components, React Compiler, and Vitest browser mode. Marked declining patterns (Redux, CSS-in-JS). Track the TypeScript roadmap (Corsa/tsgo) separately."
},
"categories": {
"nextjs": [
{
"name": "Next.js 16 Documentation",
"url": "https://nextjs.org/docs",
"type": "framework",
"relevance": "Official Next.js 16 docs with Turbopack, enhanced ISR/SSR/SSG, partial prerendering.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js App Router",
"url": "https://nextjs.org/docs/app",
"type": "documentation",
"relevance": "App Router architecture, layouts, loading states, error handling.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js Server Actions",
"url": "https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations",
"type": "documentation",
"relevance": "Server-side mutations, form handling, revalidation.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js Image Optimization",
"url": "https://nextjs.org/docs/app/building-your-application/optimizing/images",
"type": "documentation",
"relevance": "Image component, optimization, responsive images.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "Next.js Examples",
"url": "https://github.com/vercel/next.js/tree/canary/examples",
"type": "examples",
"relevance": "Official Next.js example applications for various use cases.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "Next.js 16 Blog Post",
"url": "https://nextjs.org/blog/next-16",
"type": "announcement",
"relevance": "Official Next.js 16 release notes - Turbopack, React 19 support, new features.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js 16 Upgrade Guide",
"url": "https://nextjs.org/docs/app/guides/upgrading/version-16",
"type": "documentation",
"relevance": "Complete migration guide from Next.js 15 to 16 - middleware->proxy, async APIs, Turbopack, image changes.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js Proxy Documentation",
"url": "https://nextjs.org/docs/app/getting-started/proxy",
"type": "documentation",
"relevance": "New proxy.ts convention replacing middleware.ts - runs on Node.js runtime, network boundary patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js proxy.js File Convention",
"url": "https://nextjs.org/docs/app/api-reference/file-conventions/proxy",
"type": "reference",
"relevance": "API reference for proxy.ts file convention - export function, config, matcher patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js Codemods",
"url": "https://nextjs.org/docs/app/guides/upgrading/codemods",
"type": "tooling",
"relevance": "Automated migration tools for Next.js upgrades - middleware->proxy, async APIs, Turbopack config.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
}
],
"react": [
{
"name": "React Documentation",
"url": "https://react.dev/",
"type": "library",
"relevance": "Official React docs, hooks, components, best practices.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "React 19.2 Release Notes",
"url": "https://react.dev/blog/2025/10/01/react-19-2",
"type": "announcement",
"relevance": "React 19.2 features: Partial Prerendering, performance improvements, stable APIs.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Critical Security Vulnerability in React Server Components (Dec 2025)",
"url": "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components",
"type": "announcement",
"relevance": "Security advisory and patched versions for an unauthenticated RCE in React Server Components.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Denial of Service and Source Code Exposure in React Server Components (Dec 2025)",
"url": "https://react.dev/blog/2025/12/11/denial-of-service-and-source-code-exposure-in-react-server-components",
"type": "announcement",
"relevance": "Follow-up advisories for additional vulnerabilities in React Server Components and mitigations.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "React Server Components",
"url": "https://react.dev/reference/rsc/server-components",
"type": "documentation",
"relevance": "Server Components, 'use client', 'use server', composition patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "React use() Hook",
"url": "https://react.dev/reference/react/use",
"type": "reference",
"relevance": "React 19 use() hook for promise resolution with Suspense support.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "React Hooks Reference",
"url": "https://react.dev/reference/react",
"type": "reference",
"relevance": "Complete hooks reference, useState, useEffect, custom hooks.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "Josh Comeau - Server Components Guide",
"url": "https://www.joshwcomeau.com/react/server-components/",
"type": "guide",
"relevance": "Comprehensive mental model for React Server Components.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
}
],
"typescript": [
{
"name": "TypeScript Documentation",
"url": "https://www.typescriptlang.org/docs/",
"type": "language",
"relevance": "TypeScript language reference, types, generics, advanced patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "React TypeScript Cheatsheet",
"url": "https://react-typescript-cheatsheet.netlify.app/",
"type": "guide",
"relevance": "TypeScript patterns for React components, hooks, props.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "TypeScript Handbook",
"url": "https://www.typescriptlang.org/docs/handbook/",
"type": "documentation",
"relevance": "TypeScript 5.x handbook with satisfies operator, template literal types.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "TypeScript 5.9 Release Notes",
"url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-9/",
"type": "announcement",
"relevance": "TypeScript 5.9 features: import defer, expandable hovers, performance improvements.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "TypeScript Roadmap - Corsa (tsgo)",
"url": "https://www.infoq.com/news/2026/01/typescript-7-progress/",
"type": "announcement",
"relevance": "Track the TypeScript compiler rewrite (tsgo/Corsa) and timeline. Verify current status and release targets.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
}
],
"tailwind_css": [
{
"name": "Tailwind CSS Documentation",
"url": "https://tailwindcss.com/docs",
"type": "framework",
"relevance": "Utility-first CSS framework, responsive design, customization.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Tailwind UI Components",
"url": "https://tailwindui.com/components",
"type": "components",
"relevance": "Pre-built component examples (some free, some paid).",
"update_frequency": "active",
"access": "freemium",
"add_as_web_search": false
},
{
"name": "Headless UI",
"url": "https://headlessui.com/",
"type": "library",
"relevance": "Unstyled, accessible UI components for Tailwind CSS.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"shadcn_ui": [
{
"name": "shadcn/ui Documentation",
"url": "https://ui.shadcn.com/",
"type": "components",
"relevance": "Re-usable components built with Radix UI and Tailwind CSS.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Radix UI Primitives",
"url": "https://www.radix-ui.com/primitives",
"type": "library",
"relevance": "Unstyled, accessible components (basis of shadcn/ui).",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"forms_validation": [
{
"name": "React Hook Form",
"url": "https://react-hook-form.com/",
"type": "library",
"relevance": "Performant form library with validation, TypeScript support.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "Zod",
"url": "https://zod.dev/",
"type": "library",
"relevance": "TypeScript-first schema validation for forms and APIs.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "Formik",
"url": "https://formik.org/",
"type": "library",
"relevance": "Alternative form library for React.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"data_fetching": [
{
"name": "tRPC Documentation",
"url": "https://trpc.io/docs",
"type": "library",
"relevance": "End-to-end type-safe APIs for full-stack TypeScript. Eliminates REST/GraphQL boilerplate.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "SWR Documentation",
"url": "https://swr.vercel.app/",
"type": "library",
"relevance": "React hooks for data fetching, caching, revalidation (by Vercel).",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "TanStack Query",
"url": "https://tanstack.com/query/latest",
"type": "library",
"relevance": "Powerful async state management for data fetching (formerly React Query).",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Axios",
"url": "https://axios-http.com/",
"type": "library",
"relevance": "Promise-based HTTP client for browsers and Node.js.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"state_management": [
{
"name": "Zustand",
"url": "https://zustand-demo.pmnd.rs/",
"type": "library",
"relevance": "Lightweight state management for React - recommended Redux alternative.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "Recoil",
"url": "https://recoiljs.org/",
"type": "library",
"relevance": "Meta's state management library - atomic, flexible, modern Redux alternative.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "Jotai",
"url": "https://jotai.org/",
"type": "library",
"relevance": "Primitive and flexible state management for React.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Redux Toolkit",
"url": "https://redux-toolkit.js.org/",
"type": "library",
"relevance": "Official Redux state management (for large-scale apps).",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"authentication": [
{
"name": "NextAuth.js",
"url": "https://next-auth.js.org/",
"type": "library",
"relevance": "Authentication for Next.js (OAuth, credentials, JWT).",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Clerk",
"url": "https://clerk.com/docs",
"type": "service",
"relevance": "Complete user management for Next.js.",
"update_frequency": "continuous",
"access": "freemium",
"add_as_web_search": false
},
{
"name": "Auth0",
"url": "https://auth0.com/docs",
"type": "service",
"relevance": "Authentication and authorization platform.",
"update_frequency": "continuous",
"access": "freemium",
"add_as_web_search": false
}
],
"animation": [
{
"name": "Framer Motion",
"url": "https://www.framer.com/motion/",
"type": "library",
"relevance": "Production-ready animations for React.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "tailwindcss-animate",
"url": "https://github.com/jamiebuilds/tailwindcss-animate",
"type": "plugin",
"relevance": "Animation utilities for Tailwind CSS.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "React Spring",
"url": "https://www.react-spring.dev/",
"type": "library",
"relevance": "Spring-physics based animations for React.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"testing": [
{
"name": "Vitest",
"url": "https://vitest.dev/",
"type": "testing",
"relevance": "Fast unit testing framework (Vite-powered, Jest-compatible).",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Vitest Browser Mode",
"url": "https://vitest.dev/guide/browser/component-testing",
"type": "testing",
"relevance": "Vitest 4.0 stable browser mode for real browser component testing with Playwright/WebdriverIO.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Testing Library",
"url": "https://testing-library.com/docs/react-testing-library/intro/",
"type": "testing",
"relevance": "User-centric testing for React components.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "Playwright",
"url": "https://playwright.dev/",
"type": "testing",
"relevance": "E2E testing for web applications.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Jest",
"url": "https://jestjs.io/",
"type": "testing",
"relevance": "Popular JavaScript testing framework.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"accessibility": [
{
"name": "WCAG 2.2 Guidelines",
"url": "https://www.w3.org/WAI/WCAG22/quickref/",
"type": "standard",
"relevance": "Web Content Accessibility Guidelines quick reference.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": false
},
{
"name": "MDN Accessibility",
"url": "https://developer.mozilla.org/en-US/docs/Web/Accessibility",
"type": "documentation",
"relevance": "Comprehensive accessibility documentation and tutorials.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "axe DevTools",
"url": "https://www.deque.com/axe/devtools/",
"type": "tool",
"relevance": "Browser extension for accessibility testing.",
"update_frequency": "active",
"access": "freemium",
"add_as_web_search": false
}
],
"icons_assets": [
{
"name": "Lucide Icons",
"url": "https://lucide.dev/",
"type": "icons",
"relevance": "Icon library (default for shadcn/ui).",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Heroicons",
"url": "https://heroicons.com/",
"type": "icons",
"relevance": "SVG icons by Tailwind CSS creators.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "React Icons",
"url": "https://react-icons.github.io/react-icons/",
"type": "library",
"relevance": "Popular icon library for React.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"utilities": [
{
"name": "clsx",
"url": "https://github.com/lukeed/clsx",
"type": "utility",
"relevance": "Utility for constructing className strings conditionally.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "class-variance-authority",
"url": "https://cva.style/docs",
"type": "utility",
"relevance": "Component variants for TypeScript (used in shadcn/ui).",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "date-fns",
"url": "https://date-fns.org/",
"type": "utility",
"relevance": "Modern date utility library.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"deployment": [
{
"name": "Vercel Documentation",
"url": "https://vercel.com/docs",
"type": "platform",
"relevance": "Deployment platform for Next.js (creators of Next.js).",
"update_frequency": "continuous",
"access": "freemium",
"add_as_web_search": true
},
{
"name": "Netlify Documentation",
"url": "https://docs.netlify.com/",
"type": "platform",
"relevance": "Alternative deployment platform for frontend apps.",
"update_frequency": "continuous",
"access": "freemium",
"add_as_web_search": false
},
{
"name": "Vercel Edge Runtime",
"url": "https://vercel.com/docs/functions/edge-functions",
"type": "platform",
"relevance": "Edge deployment for global performance - serverless functions at the edge.",
"update_frequency": "continuous",
"access": "freemium",
"add_as_web_search": true
},
{
"name": "Cloudflare Workers",
"url": "https://developers.cloudflare.com/workers/",
"type": "platform",
"relevance": "Serverless edge computing platform for Next.js and frontend apps.",
"update_frequency": "continuous",
"access": "freemium",
"add_as_web_search": false
}
],
"performance": [
{
"name": "web.dev Performance",
"url": "https://web.dev/performance/",
"type": "guide",
"relevance": "Web performance best practices by Google.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Core Web Vitals",
"url": "https://web.dev/vitals/",
"type": "guide",
"relevance": "Definitions, measurement guidance, and thresholds for LCP/INP/CLS.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Next.js Performance",
"url": "https://nextjs.org/docs/app/building-your-application/optimizing",
"type": "documentation",
"relevance": "Next.js-specific performance optimization techniques.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
}
],
"best_practices": [
{
"name": "Next.js Best Practices",
"url": "https://nextjs.org/docs/app/building-your-application/routing/route-handlers",
"type": "guide",
"relevance": "Official Next.js patterns and best practices.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "React Patterns",
"url": "https://www.patterns.dev/react",
"type": "guide",
"relevance": "Design patterns for React applications.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Bulletproof React",
"url": "https://github.com/alan2207/bulletproof-react",
"type": "guide",
"relevance": "Scalable React application architecture patterns.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Frontend Trends 2026 - Syncfusion",
"url": "https://www.syncfusion.com/blogs/post/frontend-development-trends",
"type": "guide",
"relevance": "2026 frontend trends: AI-powered development, TypeScript standard, server-first architecture.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "8 Trends Web Dev 2026 - LogRocket",
"url": "https://blog.logrocket.com/8-trends-web-dev-2026/",
"type": "guide",
"relevance": "Web development trends 2026: edge computing, meta-frameworks, architecture as differentiator.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Frontend Trends Declining 2026",
"url": "https://levelup.gitconnected.com/frontend-trends-that-will-not-survive-2026-ce90e0276264",
"type": "guide",
"relevance": "Declining patterns in 2026: Redux, CSS-in-JS, Create React App, micro frontends.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Playwright Best Practices 2026",
"url": "https://www.browserstack.com/guide/playwright-best-practices",
"type": "guide",
"relevance": "15 Playwright testing best practices: semantic selectors, isolation, test design.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
}
],
"vue_nuxt": [
{
"name": "Vue 3 Documentation",
"url": "https://vuejs.org/",
"type": "framework",
"relevance": "Official Vue 3 docs with Composition API, script setup, reactive patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Nuxt Documentation",
"url": "https://nuxt.com/docs",
"type": "framework",
"relevance": "Nuxt full-stack framework docs (Nuxt 4+). Covers auto-imports, Nitro server, file-based routing, SSR/SSG, and modules.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Nuxt 4 Roadmap",
"url": "https://nuxt.com/blog/roadmap-2025",
"type": "announcement",
"relevance": "Nuxt 4 features and timeline (June 2025), plus migration notes for teams currently on Nuxt 3.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Pinia (Vue State Management)",
"url": "https://pinia.vuejs.org/",
"type": "library",
"relevance": "Official Vue state management, TypeScript support, devtools.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "VueUse",
"url": "https://vueuse.org/",
"type": "library",
"relevance": "Collection of Vue Composition utilities (200+ composables).",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "Nuxt UI",
"url": "https://ui.nuxt.com/",
"type": "components",
"relevance": "Official Nuxt component library with dark mode, icons, forms.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Vue Mastery",
"url": "https://www.vuemastery.com/",
"type": "learning",
"relevance": "Official Vue learning platform with courses and tutorials.",
"update_frequency": "active",
"access": "freemium",
"add_as_web_search": false
},
{
"name": "Mastering Nuxt",
"url": "https://masteringnuxt.com/",
"type": "learning",
"relevance": "Official Nuxt courses including Nuxt 4 full-stack unleashed.",
"update_frequency": "active",
"access": "paid",
"add_as_web_search": false
}
],
"angular": [
{
"name": "Angular Documentation",
"url": "https://angular.dev/",
"type": "framework",
"relevance": "Official Angular docs with standalone components, signals, modern patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Angular Signals Guide",
"url": "https://angular.dev/guide/signals",
"type": "documentation",
"relevance": "Fine-grained reactivity with signals, computed values, effects.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Angular Release Notes",
"url": "https://angular.dev/guide/releases",
"type": "documentation",
"relevance": "Angular release notes and breaking changes. Use before upgrades and when validating version-specific patterns.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Angular Material",
"url": "https://material.angular.io/",
"type": "components",
"relevance": "Official Material Design component library for Angular.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "PrimeNG",
"url": "https://primeng.org/",
"type": "components",
"relevance": "Rich UI component library for Angular with 90+ components.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Angular CLI",
"url": "https://angular.dev/cli",
"type": "tooling",
"relevance": "Command-line interface for Angular development.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "RxJS Documentation",
"url": "https://rxjs.dev/",
"type": "library",
"relevance": "Reactive programming library used extensively in Angular.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Angular 2025 Strategy",
"url": "https://blog.angular.dev/angular-2025-strategy-9ca333dfc334",
"type": "announcement",
"relevance": "Angular team's 2025 vision - zoneless, signals, performance.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
}
],
"svelte_sveltekit": [
{
"name": "Svelte 5 Documentation",
"url": "https://svelte.dev/",
"type": "framework",
"relevance": "Svelte 5 with runes reactivity system ($state, $derived, $effect, $props).",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "SvelteKit Documentation",
"url": "https://kit.svelte.dev/",
"type": "framework",
"relevance": "SvelteKit full-stack framework with loaders, actions, progressive enhancement.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Runes Introduction",
"url": "https://svelte.dev/blog/runes",
"type": "announcement",
"relevance": "Official Svelte 5 runes announcement - universal reactivity.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Svelte Tutorial",
"url": "https://learn.svelte.dev/",
"type": "learning",
"relevance": "Interactive official Svelte tutorial with Svelte 5 examples.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "Svelte Society",
"url": "https://sveltesociety.dev/",
"type": "community",
"relevance": "Svelte community resources, recipes, components.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Svelte 5 Migration Guide",
"url": "https://svelte.dev/docs/svelte/v5-migration-guide",
"type": "documentation",
"relevance": "Migration from Svelte 4 to 5, breaking changes, runes adoption.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
},
{
"name": "shadcn-svelte",
"url": "https://www.shadcn-svelte.com/",
"type": "components",
"relevance": "Svelte port of shadcn/ui components.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"remix": [
{
"name": "Remix Documentation",
"url": "https://remix.run/docs",
"type": "framework",
"relevance": "Full-stack React framework with loaders, actions, progressive enhancement.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Remix Examples",
"url": "https://github.com/remix-run/examples",
"type": "examples",
"relevance": "Official Remix example applications and patterns.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Remix vs Next.js Comparison",
"url": "https://strapi.io/blog/next-js-vs-remix-2025-developer-framework-comparison-guide",
"type": "guide",
"relevance": "Technical comparison for choosing between Remix and Next.js.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": false
},
{
"name": "Remix Blog",
"url": "https://remix.run/blog",
"type": "blog",
"relevance": "Official Remix blog with updates and best practices.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": true
},
{
"name": "Remix Stacks",
"url": "https://remix.run/docs/en/main/guides/templates",
"type": "templates",
"relevance": "Official production-ready Remix templates.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
}
],
"vite": [
{
"name": "Vite Documentation",
"url": "https://vitejs.dev/",
"type": "tooling",
"relevance": "Next-generation frontend build tool with instant HMR.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": true
},
{
"name": "Vite React Plugin",
"url": "https://github.com/vitejs/vite-plugin-react",
"type": "plugin",
"relevance": "Official Vite plugin for React Fast Refresh and JSX.",
"update_frequency": "active",
"access": "free",
"add_as_web_search": false
},
{
"name": "Vite Guide 2025",
"url": "https://codeparrot.ai/blogs/advanced-guide-to-using-vite-with-react-in-2025",
"type": "guide",
"relevance": "Modern Vite + React patterns and best practices.",
"update_frequency": "static",
"access": "free",
"add_as_web_search": false
},
{
"name": "Vitest",
"url": "https://vitest.dev/",
"type": "testing",
"relevance": "Blazing fast unit testing framework powered by Vite.",
"update_frequency": "continuous",
"access": "free",
"add_as_web_search": false
}
]
},
"research_tools": [
{
"name": "React DevTools",
"url": "https://react.dev/learn/react-developer-tools",
"purpose": "Browser extension for debugging React applications.",
"use_case": "Component inspection, props debugging, performance profiling."
},
{
"name": "Tailwind CSS IntelliSense",
"url": "https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss",
"purpose": "VS Code extension for Tailwind CSS autocomplete.",
"use_case": "Class suggestions, linting, color previews."
},
{
"name": "v0 by Vercel",
"url": "https://v0.dev/",
"purpose": "AI-powered UI generation using shadcn/ui.",
"use_case": "Rapid component prototyping.",
"optional": true
},
{
"name": "Lighthouse",
"url": "https://developer.chrome.com/docs/lighthouse/",
"purpose": "Automated tool for improving web page quality.",
"use_case": "Performance, accessibility, SEO audits."
}
],
"communities": [
{
"name": "Next.js Discord",
"url": "https://nextjs.org/discord",
"platform": "discord",
"focus": "Next.js development, App Router, deployment, troubleshooting."
},
{
"name": "Reactiflux Discord",
"url": "https://www.reactiflux.com/",
"platform": "discord",
"focus": "React community, help, discussions."
},
{
"name": "Tailwind CSS Discord",
"url": "https://tailwindcss.com/discord",
"platform": "discord",
"focus": "Tailwind CSS styling, customization, best practices."
},
{
"name": "r/nextjs",
"url": "https://reddit.com/r/nextjs",
"platform": "reddit",
"focus": "Next.js community, showcase, questions."
},
{
"name": "r/reactjs",
"url": "https://reddit.com/r/reactjs",
"platform": "reddit",
"focus": "React development, patterns, news."
}
]
}
Single-File HTML Bundle Playbook - React + Tailwind + shadcn/ui
Use this when you need to share a runnable UI prototype as a single bundle.html (offline-friendly), while keeping React state/routing and accessible components.
Quick Start
1) Scaffold: npm create vite@latest my-bundle -- --template react-ts 2) Use the Vite + React template as a baseline: ../assets/vite-react/template-vite-react-ts.md 3) Produce a single-file build:
- Prefer a single-file bundling approach (for example,
vite-plugin-singlefile) when a single HTML file is a hard constraint. - Ensure code-splitting is disabled; otherwise
dist/index.htmlwill reference additional chunk files.
4) Share: ship bundle.html with no external asset dependencies.
Design Guardrails
- Avoid generic template styling: default purple gradients, over-rounded corners, centered-everything layouts, and mismatched typography.
- Use purposeful palettes, strong hierarchy, and cohesive typography.
- Leverage shadcn/ui components already installed; keep accessibility intact.
Tips
- Keep the bundle self-contained (no external fonts, images, or CDN scripts) unless explicitly allowed.
- Run the shared checklist before sharing: ../../software-clean-code-standard/assets/checklists/frontend-performance-a11y-checklist.md
- Use a supported Node.js LTS for tooling stability: https://nodejs.org/en/about/previous-releases
Optional: AI/Automation
If the bundle is generated for an LLM \"artifact\" environment, treat it as a constrained delivery target:
- Keep file size small; avoid large embedded assets unless required.
- Avoid network calls by default; make URLs explicit and configurable.
- Never embed secrets or tokens in the HTML.
React 19 + Next.js 16 Production Gotchas
Seven patterns learned from real production sessions. Each pattern shows a common failure mode and the correct approach.
---
Table of Contents
1. Hydration Safety Pattern 2. Safe Storage Access (String Discriminator) 3. React Three Fiber Prop Spreading 4. Defensive Response Parsing 5. The Truthy `||` Fallback Trap 6. Turbopack + macOS File Descriptor Limit 7. Procedural Generation over External Assets (WebGL) 8. SEO-Safe UI and Copy Refresh Runbook
---
1. Hydration Safety Pattern
In Next.js 16 + React 19 SSR, server components run in Node.js (UTC, no window) while client components hydrate in the browser. Every new Date(), localStorage, and browser API call is a potential mismatch.
// PASS: useState(null) + useEffect — server renders skeleton, client fills real value
const [moonPhase, setMoonPhase] = useState<string | null>(null);
useEffect(() => {
setMoonPhase(calculateMoonPhase(new Date()));
}, []);
if (!moonPhase) return <Skeleton />;
// FAIL: useMemo with Date() — server (UTC midnight) !== client (user timezone)
const moonPhase = useMemo(() => calculateMoonPhase(new Date()), []);
// Causes React Error #418 (hydration mismatch), 53 occurrences in productionRule: Use useState(null) + useEffect for ANY computation depending on:
new Date()(timezone-dependent)localStorage/sessionStorage(not available on server)window.*properties (navigator, screen, location)- Any browser-only API
---
2. Safe Storage Access (String Discriminator Pattern)
JavaScript evaluates function arguments BEFORE the function body executes. Passing localStorage to a safe wrapper defeats the try/catch:
// FAIL: localStorage is evaluated at the CALL SITE, before try/catch
function safeGet(storage: Storage, key: string) {
try { return storage.getItem(key); } // too late — already threw
catch { return null; }
}
safeGet(localStorage, 'theme'); // SecurityError in Firefox (cookies disabled)
// PASS: String discriminator — storage access inside try/catch
function safeGet(type: 'local' | 'session', key: string) {
try {
const storage = type === 'local' ? window.localStorage : window.sessionStorage;
return storage.getItem(key);
} catch { return null; }
}
safeGet('local', 'theme'); // Safe — never throws---
3. React Three Fiber (R3F) Prop Spreading
Never rest-spread props onto R3F/Three.js elements. Unknown props corrupt Three.js internal state silently:
// FAIL: Spreads isHovered, color, etc. onto <mesh> — breaks click handlers
<mesh {...handlers} position={pos}>
// PASS: Destructure and pass only known R3F event props
const { onClick, onPointerOver, onPointerOut } = handlers;
<mesh onClick={onClick} onPointerOver={onPointerOver} onPointerOut={onPointerOut} position={pos}>---
4. Defensive Response Parsing
Dev servers, CDNs, and proxies can return HTML error pages. Never call .json() without guards:
// FAIL: Throws SyntaxError when server returns HTML error page
const data = await response.json();
// PASS: Check response.ok + try/catch json()
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
let data;
try {
data = await response.json();
} catch {
throw new Error('Invalid JSON response — server may have returned an error page');
}---
5. The Truthy || Fallback Trap
data.field || [] does NOT protect against truthy non-array objects:
// FAIL: { __gated: true, teaser: "..." } is truthy — passes through as "the array"
const items = data.transits || [];
items.sort(); // TypeError: items.sort is not a function
// PASS: Array.isArray() at system boundaries
const items = Array.isArray(data.transits) ? data.transits : [];---
6. Turbopack + macOS File Descriptor Limit
macOS default ulimit (~256) is too low for Turbopack in large Next.js projects. Causes:
EMFILE: too many open fileserrorsbuild-manifest.jsonENOENT panics- Stale chunk loading failures in browser
Fix:
# Add to ~/.zshrc or ~/.bashrc
ulimit -n 10240
# Emergency recovery when .next is corrupted
# 1. Kill dev server
# 2. rm -rf .next
# 3. npm run dev---
7. Procedural Generation over External Assets (WebGL)
For WebGL/Three.js visuals, procedural generation (GLSL shaders) is more robust than external texture files:
- No 404 errors from missing textures
- No sandbox/CORS issues
- No loading states or error cascades
- Zero external file dependencies
- Often more visually striking (simplex noise patterns)
---
SEO-Safe UI and Copy Refresh Runbook
Use this for redesigns, pricing-copy updates, and landing refreshes that must not break indexed routes.
Command Checklist
# 1) Verify route/link impact
rg -n "href=|router\.push\(|redirect\(" src app
# 2) Verify metadata/sitemap/robots touchpoints
rg -n "metadata|sitemap|robots|canonical|hreflang|alternates" src app
# 3) Sweep for stale phrases (pricing/trial/campaign copy)
rg -n "free trial|7-day|old-price|legacy-plan-name" src/messages src/components
# 4) Build to catch route/import regressions
npm run buildNo-Regressions Rules
- Do not remove or rename indexed routes without explicit redirect mapping.
- Keep locale routes and metadata aligned; no mixed-language metadata.
- Update copy and analytics labels together when pricing language changes.
- Run link audit after deleting/renaming components used by navigation cards.