Scaffolds React/Next.js projects with TypeScript/Tailwind, generates components/hooks, analyzes/optimizes bundles, implements React patterns, ensures accessibility/testing.
From antigravity-awesome-skillsnpx claudepluginhub sickn33/antigravity-awesome-skills --plugin antigravity-awesome-skillsThis skill uses the workspace's default tool permissions.
references/frontend_best_practices.mdreferences/nextjs_optimization_guide.mdreferences/react_patterns.mdscripts/bundle_analyzer.pyscripts/component_generator.pyscripts/frontend_scaffolder.pyDesigns and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Frontend development patterns, performance optimization, and automation tools for React/Next.js applications.
Generate a new Next.js or React project with TypeScript, Tailwind CSS, and best practice configurations.
Run the scaffolder with your project name and template:
python scripts/frontend_scaffolder.py my-app --template nextjs
Add optional features (auth, api, forms, testing, storybook):
python scripts/frontend_scaffolder.py dashboard --template nextjs --features auth,api
Navigate to the project and install dependencies:
cd my-app && npm install
Start the development server:
npm run dev
| Option | Description |
|---|---|
--template nextjs | Next.js 14+ with App Router and Server Components |
--template react | React + Vite with TypeScript |
--features auth | Add NextAuth.js authentication |
--features api | Add React Query + API client |
--features forms | Add React Hook Form + Zod validation |
--features testing | Add Vitest + Testing Library |
--dry-run | Preview files without creating them |
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
Generate React components with TypeScript, tests, and Storybook stories.
Generate a client component:
python scripts/component_generator.py Button --dir src/components/ui
Generate a server component:
python scripts/component_generator.py ProductCard --type server
Generate with test and story files:
python scripts/component_generator.py UserProfile --with-test --with-story
Generate a custom hook:
python scripts/component_generator.py FormValidation --type hook
| Option | Description |
|---|---|
--type client | Client component with 'use client' (default) |
--type server | Async server component |
--type hook | Custom React hook |
--with-test | Include test file |
--with-story | Include Storybook story |
--flat | Create in output dir without subdirectory |
--dry-run | Preview without creating files |
"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>;
}
Analyze package.json and project structure for bundle optimization opportunities.
Run the analyzer on your project:
python scripts/bundle_analyzer.py /path/to/project
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
Apply the recommended fixes by replacing heavy dependencies.
Re-run with verbose mode to check import patterns:
python scripts/bundle_analyzer.py . --verbose
| Score | Grade | Action |
|---|---|---|
| 90-100 | A | Bundle is well-optimized |
| 80-89 | B | Minor optimizations available |
| 70-79 | C | Replace heavy dependencies |
| 60-69 | D | Multiple issues need attention |
| 0-59 | F | Critical bundle size problems |
The analyzer identifies these common heavy packages:
| Package | Size | Alternative |
|---|---|---|
| moment | 290KB | date-fns (12KB) or dayjs (2KB) |
| lodash | 71KB | lodash-es with tree-shaking |
| axios | 14KB | Native fetch or ky (3KB) |
| jquery | 87KB | Native DOM APIs |
| @mui/material | Large | shadcn/ui or Radix UI |
Reference: references/react_patterns.md
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>;
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);
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} />
}
/>;
Reference: references/nextjs_optimization_guide.md
Use Server Components by default. Add 'use client' only when you need:
// 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>;
}
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>
// 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>
);
}
Reference: references/frontend_best_practices.md
<button>, <nav>, <main>)// 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>
// 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");
});
// next.config.js
const nextConfig = {
images: {
remotePatterns: [{ hostname: "cdn.example.com" }],
formats: ["image/avif", "image/webp"],
},
experimental: {
optimizePackageImports: ["lucide-react", "@heroicons/react"],
},
};
// 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",
)}
/>;
// 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>;
}
references/react_patterns.mdreferences/nextjs_optimization_guide.mdreferences/frontend_best_practices.md