Senior Frontend

Frontend development skill for React, Next.js, TypeScript, and Tailwind CSS applications. Use when building React components, optimizing Next.js performance, analyzing bundle sizes, scaffolding frontend projects, implementing accessibility, or reviewing frontend code quality.

Published by @Alireza Rezvani·0 agent reads / 30d·0 saves·

Senior Frontend

Frontend development patterns, performance optimization, and automation tools for React/Next.js applications.

Table of Contents

  • Project Scaffolding
  • Component Generation
  • Bundle Analysis
  • React Patterns
  • Next.js Optimization
  • Accessibility and Testing

Project Scaffolding

Generate a new Next.js or React project with TypeScript, Tailwind CSS, and best practice configurations.

Workflow: Create New Frontend Project

  1. Run the scaffolder with your project name and template:

    python scripts/frontend_scaffolder.py my-app --template nextjs
    
  2. Add optional features (auth, api, forms, testing, storybook):

    python scripts/frontend_scaffolder.py dashboard --template nextjs --features auth,api
    
  3. Navigate to the project and install dependencies:

    cd my-app && npm install
    
  4. Start the development server:

    npm run dev
    

Scaffolder Options

OptionDescription
--template nextjsNext.js 14+ with App Router and Server Components
--template reactReact + Vite with TypeScript
--features authAdd NextAuth.js authentication
--features apiAdd React Query + API client
--features formsAdd React Hook Form + Zod validation
--features testingAdd Vitest + Testing Library
--dry-runPreview files without creating them

Generated Structure (Next.js)

my-app/
├── app/
│   ├── layout.tsx        # Root layout with fonts
│   ├── page.tsx          # Home page
│   ├── globals.css       # Tailwind + CSS variables
│   └── api/health/route.ts
├── components/
│   ├── ui/               # Button, Input, Card
│   └── layout/           # Header, Footer, Sidebar
├── hooks/                # useDebounce, useLocalStorage
├── lib/                  # utils (cn), constants
├── types/                # TypeScript interfaces
├── tailwind.config.ts
├── next.config.js
└── package.json

Component Generation

Generate React components with TypeScript, tests, and Storybook stories.

Workflow: Create a New Component

  1. Generate a client component:

    python scripts/component_generator.py Button --dir src/components/ui
    
  2. Generate a server component:

    python scripts/component_generator.py ProductCard --type server
    
  3. Generate with test and story files:

    python scripts/component_generator.py UserProfile --with-test --with-story
    
  4. Generate a custom hook:

    python scripts/component_generator.py FormValidation --type hook
    

Generator Options

OptionDescription
--type clientClient component with 'use client' (default)
--type serverAsync server component
--type hookCustom React hook
--with-testInclude test file
--with-storyInclude Storybook story
--flatCreate in output dir without subdirectory
--dry-runPreview without creating files

Generated Component Example

'use client';

import { useState } from 'react';
import { cn } from '@/lib/utils';

interface ButtonProps {
  className?: string;
  children?: React.ReactNode;
}

export function Button({ className, children }: ButtonProps) {
  return (
    <div className={cn('', className)}>
      {children}
    </div>
  );
}

Bundle Analysis

Analyze package.json and project structure for bundle optimization opportunities.

Workflow: Optimize Bundle Size

  1. Run the analyzer on your project:

    python scripts/bundle_analyzer.py /path/to/project
    
  2. Review the health score and issues:

    Bundle Health Score: 75/100 (C)
    
    HEAVY DEPENDENCIES:
      moment (290KB)
        Alternative: date-fns (12KB) or dayjs (2KB)
    
      lodash (71KB)
        Alternative: lodash-es with tree-shaking
    
  3. Apply the recommended fixes by replacing heavy dependencies.

  4. Re-run with verbose mode to check import patterns:

    python scripts/bundle_analyzer.py . --verbose
    

Bundle Score Interpretation

ScoreGradeAction
90-100ABundle is well-optimized
80-89BMinor optimizations available
70-79CReplace heavy dependencies
60-69DMultiple issues need attention
0-59FCritical bundle size problems

Heavy Dependencies Detected

The analyzer identifies these common heavy packages:

PackageSizeAlternative
moment290KBdate-fns (12KB) or dayjs (2KB)
lodash71KBlodash-es with tree-shaking
axios14KBNative fetch or ky (3KB)
jquery87KBNative DOM APIs
@mui/materialLargeshadcn/ui or Radix UI

React Patterns

Reference: references/react_patterns.md

Compound Components

Share state between related components:

const Tabs = ({ children }) => {
  const [active, setActive] = useState(0);
  return (
    <TabsContext.Provider value={{ active, setActive }}>
      {children}
    </TabsContext.Provider>
  );
};

Tabs.List = TabList;
Tabs.Panel = TabPanel;

// Usage
<Tabs>
  <Tabs.List>
    <Tabs.Tab>One</Tabs.Tab>
    <Tabs.Tab>Two</Tabs.Tab>
  </Tabs.List>
  <Tabs.Panel>Content 1</Tabs.Panel>
  <Tabs.Panel>Content 2</Tabs.Panel>
</Tabs>

Custom Hooks

Extract reusable logic:

function useDebounce<T>(value: T, delay = 500): T {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebouncedValue(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debouncedValue;
}

// Usage
const debouncedSearch = useDebounce(searchTerm, 300);

Render Props

Share rendering logic:

function DataFetcher({ url, render }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url).then(r => r.json()).then(setData).finally(() => setLoading(false));
  }, [url]);

  return render({ data, loading });
}

// Usage
<DataFetcher
  url="/api/users"
  render={({ data, loading }) =>
    loading ? <Spinner /> : <UserList users={data} />
  }
/>

Next.js Optimization

Reference: references/nextjs_optimization_guide.md

Server vs Client Components

Use Server Components by default. Add 'use client' only when you need:

  • Event handlers (onClick, onChange)
  • State (useState, useReducer)
  • Effects (useEffect)
  • Browser APIs
// Server Component (default) - no 'use client'
async function ProductPage({ params }) {
  const product = await getProduct(params.id);  // Server-side fetch

  return (
    <div>
      <h1>{product.name}</h1>
      <AddToCartButton productId={product.id} />  {/* Client component */}
    </div>
  );
}

// Client Component
'use client';
function AddToCartButton({ productId }) {
  const [adding, setAdding] = useState(false);
  return <button onClick={() => addToCart(productId)}>Add</button>;
}

Image Optimization

import Image from 'next/image';

// Above the fold - load immediately
<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority
/>

// Responsive image with fill
<div className="relative aspect-video">
  <Image
    src="/product.jpg"
    alt="Product"
    fill
    sizes="(max-width: 768px) 100vw, 50vw"
    className="object-cover"
  />
</div>

Data Fetching Patterns

// Parallel fetching
async function Dashboard() {
  const [user, stats] = await Promise.all([
    getUser(),
    getStats()
  ]);
  return <div>...</div>;
}

// Streaming with Suspense
async function ProductPage({ params }) {
  return (
    <div>
      <ProductDetails id={params.id} />
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews productId={params.id} />
      </Suspense>
    </div>
  );
}

Accessibility and Testing

Reference: references/frontend_best_practices.md

Accessibility Checklist

  1. Semantic HTML: Use proper elements (<button>, <nav>, <main>)
  2. Keyboard Navigation: All interactive elements focusable
  3. ARIA Labels: Provide labels for icons and complex widgets
  4. Color Contrast: Minimum 4.5:1 for normal text
  5. Focus Indicators: Visible focus states
// Accessible button
<button
  type="button"
  aria-label="Close dialog"
  onClick={onClose}
  className="focus-visible:ring-2 focus-visible:ring-blue-500"
>
  <XIcon aria-hidden="true" />
</button>

// Skip link for keyboard users
<a href="#main-content" className="sr-only focus:not-sr-only">
  Skip to main content
</a>

Testing Strategy

// Component test with React Testing Library
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

test('button triggers action on click', async () => {
  const onClick = vi.fn();
  render(<Button onClick={onClick}>Click me</Button>);

  await userEvent.click(screen.getByRole('button'));
  expect(onClick).toHaveBeenCalledTimes(1);
});

// Test accessibility
test('dialog is accessible', async () => {
  render(<Dialog open={true} title="Confirm" />);

  expect(screen.getByRole('dialog')).toBeInTheDocument();
  expect(screen.getByRole('dialog')).toHaveAttribute('aria-labelledby');
});

Quick Reference

Common Next.js Config

// next.config.js
const nextConfig = {
  images: {
    remotePatterns: [{ protocol: 'https', hostname: 'cdn.example.com' }],
    formats: ['image/avif', 'image/webp'],
  },
  experimental: {
    optimizePackageImports: ['lucide-react', '@heroicons/react'],
  },
};

Tailwind CSS Utilities

// Conditional classes with cn()
import { cn } from '@/lib/utils';

<button className={cn(
  'px-4 py-2 rounded',
  variant === 'primary' && 'bg-blue-500 text-white',
  disabled && 'opacity-50 cursor-not-allowed'
)} />

TypeScript Patterns

// Props with children
interface CardProps {
  className?: string;
  children: React.ReactNode;
}

// Generic component
interface ListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
  return <ul>{items.map(renderItem)}</ul>;
}

Resources

  • React Patterns: references/react_patterns.md
  • Next.js Optimization: references/nextjs_optimization_guide.md
  • Best Practices: references/frontend_best_practices.md
  • Forcing-question library (Matt Pocock grill): references/forcing_questions.md
  • Composition map (which specialist to fork into): references/composition_map.md

Assumptions and Verifiable Success Criteria (Karpathy discipline)

Before this skill scaffolds a component, recommends a framework, or audits a bundle, the following four assumptions MUST be surfaced.

  1. Primary user device + network — mobile-4G, desktop-fiber, low-end-Android, or corporate-network. Drives every perf decision.
  2. LCP target in milliseconds — a single number, not "fast." Drives bundle budget and rendering choice.
  3. SEO-dependent vs. auth-walled — drives rendering (SSR/SSG/RSC vs. SPA).
  4. WCAG target + named a11y owner — AA, AAA, or best-effort. Drives a11y investment and CI gates.

Verifiable success criteria (Karpathy #4) — every recommendation must include:

  • Core Web Vitals targets (LCP, INP, CLS) at p75 on the primary device
  • A per-route JS bundle budget in KB-gzip
  • A Lighthouse a11y floor + perf floor

If any of those three is not stated, the recommendation is incomplete — return to Q2 of the forcing-question library.

The scripts/frontend_decision_engine.py tool encodes these checks: it refuses to recommend a profile without the four assumption inputs and prints the verifiable thresholds for the matched profile.


Customization profiles

Four built-in profiles in profiles/ calibrate every recommendation:

ProfileWhen to pickLCP target (mobile-4G p75)Bundle budget
next-app-routerSaaS customer-facing, SEO + dynamic, RSC-first2000ms150 KB-gzip / route
remix-or-sveltekitMobile-4G primary, low-JS-first, progressive enhancement1500ms80 KB-gzip / route
vite-spaAuth-walled app, desktop/corporate primary2500ms200 KB init + 80 KB / route
astro-or-staticMarketing / docs / blog, near-zero write, SEO-critical1200ms30 KB JS / page

Pick a profile via:

python scripts/frontend_decision_engine.py \
  --primary-device mobile-4g --lcp-target-ms 2000 \
  --seo-dependent true --auth-walled false --team-size 5

The tool returns the best-fit profile, the runner-up tradeoff (if within 15%), the stack picks, the anti-patterns to avoid on that profile, and the required CI gates.

To add a custom profile (e.g., your org's internal-tool defaults): copy profiles/vite-spa.json to profiles/<your-org>.json and adjust constraints + success_thresholds.


Composition map

This skill does NOT reimplement scope owned by the POWERFUL-tier specialists. It forks into them. See references/composition_map.md for the full routing table. Key forks:

ConcernFork into
WCAG audit, contrast, screen-readerengineering-team/skills/a11y-audit/
Bundle profiling + runtime perfengineering/skills/performance-profiler/
Cinematic / scroll-storytelling landingengineering-team/skills/epic-design/
Apple HIG (iOS / macOS / visionOS)product-team/skills/apple-hig-expert/
Pre-commit Karpathy reviewengineering/karpathy-coder/
Pre-flight architecture grillengineering/grill-me/

The cs-frontend-engineer agent orchestrates these forks via context: fork. Invoke it from another agent with Agent({subagent_type: "cs-frontend-engineer", prompt: "..."}) or via /cs:frontend-review <your problem>.


Forcing-question library (Matt Pocock grill)

Before locking any framework or rendering decision, walk the seven forcing questions in references/forcing_questions.md. Discipline:

  1. One question per turn. No bundling.
  2. Always recommend the answer with cited canon.
  3. Track answers in /tmp/frontend-grill-<date>.md.
  4. If a kill criterion trips, stop. Don't scaffold around an unresolved gap.
  5. After Q7, run frontend_decision_engine.py with the seven answers.

Summary:

  1. Primary device + network?
  2. LCP target in ms (and INP, CLS)?
  3. RSC / SPA / SSR / SSG — pick and defend?
  4. JS bundle budget per route?
  5. SEO-dependent or auth-walled?
  6. Design-system source of truth?
  7. WCAG target + named a11y owner?

Invocation from other agents and skills

Three surfaces:

  1. Slash command: /cs:frontend-review <prompt> — full grill + decision engine + composition routing.
  2. Agent subagent: Agent({subagent_type: "cs-frontend-engineer", prompt: "..."}) — forks context, returns ≤ 200-word digest.
  3. Direct tool call: python scripts/frontend_decision_engine.py ... — deterministic profile match when inputs are known.

See agents/engineering/cs-frontend-engineer.md for the full invocation contract.

Bundled with this artifact

15 files

Reference files that ship alongside this artifact. Agents pull these in only when the task needs them.

More on the bench

SKILL0

Cs Md Review

Convert a markdown PR writeup or code review (with ```diff blocks and > [!BLOCKER]/[!MAJOR]/[!MINOR]/[!NIT] severity callouts) into a single-file 2-column HTML review. Top jump-nav lists every finding by severity. Diff on the left, severity-tagged annotation cards on the right, mandatory named reviewer footer. Refuses without --reviewer (a code review must name a human) or if no diff hunks present.

software-engineering+2
0
SKILL0

Cs Design System

Run the one-time markdown-html design-system onboarding wizard. Captures brand primary/accent (HEX) + heading/body Google Fonts + design style (editorial/technical/minimal/playful) + default output directory + syntax theme + TOC behavior + optional logo/company. WCAG-AA validates body-text contrast; refuses if it fails. Stores at ~/.config/markdown-html/design-system.json (or project-scoped with --scope project). Every converter reads this config via config_loader.py before rendering.

design+2
0
SKILL0

User Research Synthesizer

Synthesize user research findings from interviews, surveys, and analytics. Create insight reports, customer journey maps, and actionable recommendations based on research data and qualitative findings.

product-management+2
0