Install
$ agentstack add skill-nembie-claude-code-skills-react-component-generator ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
React Component Generator
Before generating any output, read config/defaults.md and adapt all patterns, imports, and code examples to the user's configured stack.
Generation Process
- Determine component type and requirements
- Generate TypeScript props interface
- Create functional component with hooks
- Add accessibility attributes
- Include keyboard navigation where applicable
Component Types
Data Display Component
interface DataTableProps {
data: T[];
columns: ColumnDef[];
onRowClick?: (item: T) => void;
loading?: boolean;
emptyMessage?: string;
}
function DataTable({
data,
columns,
onRowClick,
loading = false,
emptyMessage = 'No data available',
}: DataTableProps) {
if (loading) {
return Loading...;
}
if (data.length === 0) {
return {emptyMessage};
}
return (
{columns.map((col) => (
{col.header}
))}
{data.map((item, index) => (
onRowClick?.(item)}
onKeyDown={(e) => e.key === 'Enter' && onRowClick?.(item)}
tabIndex={onRowClick ? 0 : undefined}
role={onRowClick ? 'button' : undefined}
aria-rowindex={index + 1}
>
{columns.map((col) => (
{col.render(item)}
))}
))}
);
}
Form Component
interface FormFieldProps {
label: string;
name: string;
type?: 'text' | 'email' | 'password' | 'number';
value: string;
onChange: (value: string) => void;
error?: string;
required?: boolean;
disabled?: boolean;
placeholder?: string;
}
function FormField({
label,
name,
type = 'text',
value,
onChange,
error,
required = false,
disabled = false,
placeholder,
}: FormFieldProps) {
const id = `field-${name}`;
const errorId = `${id}-error`;
return (
{label}
{required && *}
onChange(e.target.value)}
disabled={disabled}
placeholder={placeholder}
required={required}
aria-required={required}
aria-invalid={!!error}
aria-describedby={error ? errorId : undefined}
/>
{error && (
{error}
)}
);
}
Modal/Dialog Component
interface ModalProps {
open: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
actions?: React.ReactNode;
}
function Modal({ open, onClose, title, children, actions }: ModalProps) {
const modalRef = useRef(null);
const previousFocus = useRef(null);
useEffect(() => {
if (open) {
previousFocus.current = document.activeElement as HTMLElement;
modalRef.current?.focus();
} else {
previousFocus.current?.focus();
}
}, [open]);
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape' && open) {
onClose();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [open, onClose]);
if (!open) return null;
return (
e.stopPropagation()}
tabIndex={-1}
>
{title}
×
{children}
{actions && {actions}}
);
}
Layout Component
interface PageLayoutProps {
children: React.ReactNode;
sidebar?: React.ReactNode;
header?: React.ReactNode;
footer?: React.ReactNode;
}
function PageLayout({ children, sidebar, header, footer }: PageLayoutProps) {
return (
{header && {header}}
{sidebar && {sidebar}}
{children}
{footer && {footer}}
);
}
Button Component
interface ButtonProps extends React.ButtonHTMLAttributes {
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
size?: 'sm' | 'md' | 'lg';
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
const Button = forwardRef(
(
{
variant = 'primary',
size = 'md',
loading = false,
leftIcon,
rightIcon,
disabled,
children,
className,
...props
},
ref
) => {
return (
{loading ? (
) : (
leftIcon
)}
{children}
{!loading && rightIcon}
);
}
);
Button.displayName = 'Button';
Composable Patterns
Compound Components
interface SelectContextValue {
value: string;
onChange: (value: string) => void;
}
const SelectContext = createContext(null);
function Select({ value, onChange, children }: {
value: string;
onChange: (value: string) => void;
children: React.ReactNode;
}) {
return (
{children}
);
}
function Option({ value, children }: { value: string; children: React.ReactNode }) {
const ctx = useContext(SelectContext);
if (!ctx) throw new Error('Option must be used within Select');
const selected = ctx.value === value;
return (
ctx.onChange(value)}
onKeyDown={(e) => e.key === 'Enter' && ctx.onChange(value)}
tabIndex={0}
>
{children}
);
}
Select.Option = Option;
Render Props
interface ToggleRenderProps {
on: boolean;
toggle: () => void;
setOn: () => void;
setOff: () => void;
}
interface ToggleProps {
initialOn?: boolean;
children: (props: ToggleRenderProps) => React.ReactNode;
}
function Toggle({ initialOn = false, children }: ToggleProps) {
const [on, setOn] = useState(initialOn);
const renderProps: ToggleRenderProps = {
on,
toggle: () => setOn((prev) => !prev),
setOn: () => setOn(true),
setOff: () => setOn(false),
};
return <>{children(renderProps)};
}
Accessibility Checklist
- [ ] Semantic HTML elements (button, nav, main, etc.)
- [ ] ARIA roles where semantic HTML insufficient
- [ ] aria-label or aria-labelledby for non-text content
- [ ] aria-describedby for error messages
- [ ] Focus management (modals, dynamic content)
- [ ] Keyboard navigation (Enter, Escape, Arrow keys)
- [ ] Visible focus indicators
- [ ] Color contrast (don't rely on color alone)
- [ ] Loading states announced (aria-busy, role="status")
- [ ] Error states announced (role="alert")
Completeness Check
After generating a component, verify completeness: does it export a TypeScript props interface? Does it handle the loading/error/empty states if it fetches data? Do interactive elements have keyboard handlers and aria attributes? If any check fails, fix the component before delivering.
Asset
See assets/component-template/Component.tsx for a minimal starter template.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Nembie
- Source: Nembie/claude-code-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.