next-js/skills/skills/component-factory/SKILL.md
Create reusable React components following the project's conventions for naming, file structure, barrel exports, Tailwind styling, and hook patterns. Use this skill whenever the user wants to create a new component, add a UI element, build a feature section, create a card/modal/widget, add a table cell renderer, or organize components into feature directories. Trigger on: "create component", "add a component", "new component", "build a widget", "create a card", "add a section", or when the user describes any reusable UI element that needs to be created.
npx skillsauth add spuneiartur/claude-agent-specs component-factoryInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create React components following the project's established conventions. The project has a consistent component architecture — follow it to maintain codebase coherence.
For general-purpose, reusable components. Lives directly in /components/.
File: components/{ComponentName}.jsx
const ComponentName = ({ prop1, prop2, className = '' }) => {
return (
<div className={`base-styles ${className}`}>
{/* Component content */}
</div>
);
};
export default ComponentName;
Then update the barrel export in components/index.js:
export { default as ComponentName } from './ComponentName';
For a group of related components (e.g., a feature section with multiple parts). Lives in /components/{FeatureName}/.
components/
FeatureName/
FeatureCard.jsx
FeatureList.jsx
FeatureHeader.jsx
index.js ← barrel exports
Barrel export (components/{FeatureName}/index.js):
export { default as FeatureCard } from './FeatureCard';
export { default as FeatureList } from './FeatureList';
export { default as FeatureHeader } from './FeatureHeader';
Import pattern:
import { FeatureCard, FeatureList } from '@components/FeatureName';
For rendering custom cells in admin tables. Lives in /components/TableCells/.
Props contract: Receives { value, row: { original } } from react-table:
const ResourceNameCell = ({ value, row: { original } }) => {
return (
<div className="flex items-center space-x-3">
<span className="font-medium text-gray-900">{value}</span>
{original.subtitle && (
<span className="text-sm text-gray-500">{original.subtitle}</span>
)}
</div>
);
};
export default ResourceNameCell;
For admin section components. Lives in /components/Admin/{Entity}/.
Standard set per entity:
{Entity}Header.jsx — page header with title, add button, filters{Entity}Table.jsx — data table with infinite query{Entity}Filters.jsx — filter form{Entity}ActionsCell.jsx — edit/delete buttons for table rows{Entity}StatusCell.jsx — status badge renderer (optional)index.js — barrel exportsSee the admin-crud-scaffold skill for complete templates.
.jsx (not .js for components)ProductCard.jsx)export default at the bottom of the fileAlways use path aliases from jsconfig.json:
| Alias | Path | Example |
|-------|------|---------|
| @components | components/index | import { Button, Modal } from '@components' |
| @components/* | components/* | import { ProductCard } from '@components/Products' |
| @hooks | hooks/index | import { useQuery, useDisclosure } from '@hooks' |
| @lib | lib/index | import { toaster, classnames } from '@lib' |
| @functions | functions/index | import { formatDate, generateSlug } from '@functions' |
| @constants | constants/index | import { DOCUMENTS } from '@constants' |
| @data | data/index | import { articleColumns } from '@data' |
| @site.config | site.config | import config from '@site.config' |
Use Tailwind CSS with the project's theme tokens:
{/* Use project theme colors */}
<div className="bg-primary text-white"> {/* Primary dark color */}
<div className="bg-accent text-gray-900"> {/* Accent gold color */}
<div className="text-muted"> {/* Muted text */}
<div className="border-metal-200"> {/* Border color */}
{/* Use project font families */}
<h1 className="font-heading"> {/* Heading font (Poppins) */}
<p className="font-body"> {/* Body font (Inter) */}
Before creating new components, check if the project already provides what you need. Import from @components:
AnimatedTitle, AreYouSure, Bone (skeleton), BrandButton, Button, CallToAction, ContextMenu, Embed, EmailMask, ErrorBoundary, ErrorFallback, FilterModal, FormattedTime, Image, Layout, Link, Loading, LoadingBubbles, Logo, MediaUpload, Modal, NoIndex, NoSsr, OpenGraph, Overflow, Pagination, Percent, Pill, Plural, PresentationLayout, Price, ProductSlider, Profile, SearchBar, SearchModal, ShowMore, Spinner, StatusCell, TikTokEmbed, Toaster, Tooltip, Trim
Common hooks from @hooks:
| Hook | Use For |
|------|---------|
| useQuery(url, options) | Fetch data |
| useInfiniteQuery(url, options) | Paginated data |
| useMutation(fn, options) | Create/update/delete |
| useDisclosure() | Modal open/close state → { isOpen, show, hide, toggle } |
| useToggle(initial) | Boolean toggle |
| useDebounce(value, delay) | Debounce search input |
| useOnClickOutside(ref, handler) | Close dropdown on outside click |
| useIntersectionObserver(options) | Lazy loading, scroll reveal |
| useSwipeable(handlers) | Touch swipe gestures |
| useCollapsible() | Accordion expand/collapse |
| useSlugGenerator(sourceValue) | Auto-generate URL slugs |
Keep files short — maximum 40-50 lines of code per file. If a component grows beyond this, split it into smaller sub-components in the same feature directory.
When using useQuery or useInfiniteQuery, create separate components for each state:
{Entity}Loading.jsx — use Bone component from @components for skeleton placeholders{Entity}Error.jsx — error message display{Entity}Success.jsx — actual content rendering// In the parent component
{status === 'loading' && <EntityLoading />}
{status === 'error' && <EntityError />}
{status === 'success' && <EntitySuccess data={data} />}
Use Font Awesome icons in HTML format: <i className="fas fa-bars"></i>
For images, use the project's Image component with lazy-blur:
import { Image } from '@components';
import { getImageUrl, getPlaceholderImageUrl } from '@functions';
<Image
src={getImageUrl(images, 'medium')}
placeholderSrc={getPlaceholderImageUrl(images)}
effect="lazy-blur"
alt="Description"
/>
If the project doesn't have the Image component yet (e.g., in a fresh starter), set it up:
Install the dependency:
npm install react-lazy-load-image-component
Create components/Image.jsx:
import { classnames } from '@lib';
import { LazyLoadImage } from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/black-and-white.css';
import 'react-lazy-load-image-component/src/effects/opacity.css';
const Image = ({ alt, src, srcSet, sizes, placeholderSrc, effect, className, wrapperClassName, ...rest }) => (
<LazyLoadImage
alt={alt}
effect={effect}
placeholderSrc={placeholderSrc}
src={src}
srcSet={srcSet}
className={classnames('w-full h-full', className)}
wrapperClassName={classnames('w-full h-full', wrapperClassName)}
sizes={sizes}
{...rest}
/>
);
export default Image;
Create css/lazy-loading-blur-effect.css:
.lazy-load-image-background.lazy-blur {
filter: blur(15px);
}
.lazy-load-image-background.lazy-blur.lazy-load-image-loaded {
filter: blur(0);
transition: filter .3s;
}
Import it in css/index.css:
@import 'lazy-loading-blur-effect.css';
Create functions/image-utils.js:
export const getImageUrl = (images, preferredSize = 'medium') => {
const sizes = [preferredSize, 'medium', 'large', 'small', 'original'];
for (const size of sizes) {
if (images?.[size]?.path) return images[size].path;
}
return '';
};
export const getPlaceholderImageUrl = (images) => images?.small?.path || '';
export const createImageSrcSet = (images) => {
const set = {};
if (images?.small?.path) set['480w'] = images.small.path;
if (images?.medium?.path) set['768w'] = images.medium.path;
if (images?.large?.path) set['1024w'] = images.large.path;
if (images?.original?.path) set['1440w'] = images.original.path;
return set;
};
Add barrel exports in components/index.js and functions/index.js.
tools
Replace with description of the skill and when Claude should use it.
tools
Comprehensive website performance audit and optimization skill. Identifies and automatically fixes performance issues including image optimization, video compression, lazy loading, Core Web Vitals, bundle size, and rendering strategy. Uses Lighthouse (via CLI or MCP when available), ffmpeg for media processing, and the project's existing Image component with blur-up lazy loading. Use this skill whenever the user mentions: website speed, page load time, performance audit, Core Web Vitals, Lighthouse, optimize images, compress videos, lazy loading, LCP, CLS, FID, INP, slow website, speed up, performance optimization, image compression, video optimization, blur placeholder, WebP conversion, media audit, bundle size, or wants to improve their website's loading performance. Also trigger when the user says "my site is slow", "optimize for speed", "reduce load time", "improve performance", or asks about image/video optimization in any context.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
tools
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.