From adlc-react-frontend
React component patterns including functional components, hooks, TypeScript interfaces, and Tailwind styling.
npx claudepluginhub sumanpapanaboina1983/adlc-accelerator-kit-pluginsThis skill uses the workspace's default tool permissions.
```tsx
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
import { memo } from 'react';
interface TodoItemProps {
id: string;
title: string;
completed: boolean;
onToggle: (id: string) => void;
onDelete: (id: string) => void;
}
function TodoItem({ id, title, completed, onToggle, onDelete }: TodoItemProps) {
return (
<li className="flex items-center gap-3 p-4 bg-white rounded-lg shadow-sm">
<input
type="checkbox"
checked={completed}
onChange={() => onToggle(id)}
className="h-5 w-5 rounded border-gray-300"
aria-label={`Mark "${title}" as ${completed ? 'incomplete' : 'complete'}`}
/>
<span className={completed ? 'line-through text-gray-400' : 'text-gray-900'}>
{title}
</span>
<button
onClick={() => onDelete(id)}
className="ml-auto text-red-500 hover:text-red-700"
aria-label={`Delete "${title}"`}
>
Delete
</button>
</li>
);
}
export default memo(TodoItem);
import { useState, useCallback } from 'react';
interface TodoInputProps {
onAdd: (title: string) => void;
}
export default function TodoInput({ onAdd }: TodoInputProps) {
const [title, setTitle] = useState('');
const [error, setError] = useState<string | null>(null);
const handleSubmit = useCallback((e: React.FormEvent) => {
e.preventDefault();
const trimmedTitle = title.trim();
if (!trimmedTitle) {
setError('Title is required');
return;
}
onAdd(trimmedTitle);
setTitle('');
setError(null);
}, [title, onAdd]);
return (
<form onSubmit={handleSubmit} className="flex gap-2">
<div className="flex-1">
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="What needs to be done?"
className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500"
aria-label="New todo title"
aria-invalid={!!error}
aria-describedby={error ? 'title-error' : undefined}
/>
{error && (
<p id="title-error" className="mt-1 text-sm text-red-500">
{error}
</p>
)}
</div>
<button
type="submit"
className="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600"
>
Add
</button>
</form>
);
}
import { useState, useEffect, useCallback } from 'react';
// Custom hook for todos
interface Todo {
id: string;
title: string;
completed: boolean;
}
interface UseTodosReturn {
todos: Todo[];
isLoading: boolean;
error: string | null;
addTodo: (title: string) => Promise<void>;
toggleTodo: (id: string) => Promise<void>;
deleteTodo: (id: string) => Promise<void>;
}
export function useTodos(): UseTodosReturn {
const [todos, setTodos] = useState<Todo[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
async function fetchTodos() {
try {
const response = await fetch('/api/todos');
if (!response.ok) throw new Error('Failed to fetch todos');
const data = await response.json();
setTodos(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Unknown error');
} finally {
setIsLoading(false);
}
}
fetchTodos();
}, []);
const addTodo = useCallback(async (title: string) => {
const response = await fetch('/api/todos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title }),
});
if (!response.ok) throw new Error('Failed to add todo');
const newTodo = await response.json();
setTodos((prev) => [...prev, newTodo]);
}, []);
const toggleTodo = useCallback(async (id: string) => {
const todo = todos.find((t) => t.id === id);
if (!todo) return;
const response = await fetch(`/api/todos/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ completed: !todo.completed }),
});
if (!response.ok) throw new Error('Failed to update todo');
setTodos((prev) =>
prev.map((t) => (t.id === id ? { ...t, completed: !t.completed } : t))
);
}, [todos]);
const deleteTodo = useCallback(async (id: string) => {
const response = await fetch(`/api/todos/${id}`, { method: 'DELETE' });
if (!response.ok) throw new Error('Failed to delete todo');
setTodos((prev) => prev.filter((t) => t.id !== id));
}, []);
return { todos, isLoading, error, addTodo, toggleTodo, deleteTodo };
}
// Component using the hook
export default function TodoList() {
const { todos, isLoading, error, addTodo, toggleTodo, deleteTodo } = useTodos();
if (isLoading) {
return <div className="text-center py-8">Loading...</div>;
}
if (error) {
return <div className="text-center py-8 text-red-500">{error}</div>;
}
return (
<div className="max-w-md mx-auto p-4">
<TodoInput onAdd={addTodo} />
<ul className="mt-4 space-y-2">
{todos.map((todo) => (
<TodoItem
key={todo.id}
{...todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
</ul>
{todos.length === 0 && (
<p className="text-center text-gray-500 mt-4">No todos yet</p>
)}
</div>
);
}
interface ButtonProps {
// Required
children: React.ReactNode;
onClick: () => void;
// Optional with defaults
variant?: 'primary' | 'secondary' | 'danger';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
className?: string;
}
export default function Button({
children,
onClick,
variant = 'primary',
size = 'md',
disabled = false,
className = '',
}: ButtonProps) {
// ...
}
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
error?: string;
}
export default function Input({ label, error, className, ...props }: InputProps) {
return (
<div>
<label className="block text-sm font-medium">{label}</label>
<input
className={`mt-1 block w-full rounded-md ${className}`}
aria-invalid={!!error}
{...props}
/>
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
</div>
);
}
interface SelectProps<T> {
options: T[];
value: T;
onChange: (value: T) => void;
getLabel: (option: T) => string;
getValue: (option: T) => string;
}
export default function Select<T>({
options,
value,
onChange,
getLabel,
getValue,
}: SelectProps<T>) {
return (
<select
value={getValue(value)}
onChange={(e) => {
const selected = options.find((o) => getValue(o) === e.target.value);
if (selected) onChange(selected);
}}
>
{options.map((option) => (
<option key={getValue(option)} value={getValue(option)}>
{getLabel(option)}
</option>
))}
</select>
);
}
// Simple state
const [count, setCount] = useState(0);
// State with explicit type
const [user, setUser] = useState<User | null>(null);
// State with initial function
const [todos, setTodos] = useState<Todo[]>(() => {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
});
// Fetch on mount
useEffect(() => {
fetchData();
}, []);
// React to dependency changes
useEffect(() => {
if (userId) {
fetchUser(userId);
}
}, [userId]);
// Cleanup
useEffect(() => {
const subscription = subscribe();
return () => subscription.unsubscribe();
}, []);
// Async effect
useEffect(() => {
let cancelled = false;
async function fetchData() {
const data = await api.getData();
if (!cancelled) {
setData(data);
}
}
fetchData();
return () => { cancelled = true; };
}, []);
// Memoize callback to prevent child re-renders
const handleClick = useCallback((id: string) => {
setSelected(id);
}, []);
// Memoize expensive computation
const sortedItems = useMemo(() => {
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// Memoize object to use as dependency
const filters = useMemo(() => ({
status,
category,
search,
}), [status, category, search]);
import { clsx } from 'clsx';
// Using clsx for conditional classes
<button
className={clsx(
'px-4 py-2 rounded-lg font-medium',
{
'bg-blue-500 text-white': variant === 'primary',
'bg-gray-200 text-gray-800': variant === 'secondary',
'bg-red-500 text-white': variant === 'danger',
'opacity-50 cursor-not-allowed': disabled,
}
)}
>
{children}
</button>
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
{
variants: {
variant: {
primary: 'bg-blue-500 text-white hover:bg-blue-600',
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
danger: 'bg-red-500 text-white hover:bg-red-600',
},
size: {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 text-base',
lg: 'h-12 px-6 text-lg',
},
},
defaultVariants: {
variant: 'primary',
size: 'md',
},
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
export default function Button({ variant, size, className, ...props }: ButtonProps) {
return <button className={buttonVariants({ variant, size, className })} {...props} />;
}
src/
├── components/
│ ├── ui/ # Reusable UI components
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ └── index.ts
│ ├── todos/ # Feature-specific components
│ │ ├── TodoItem.tsx
│ │ ├── TodoList.tsx
│ │ ├── TodoInput.tsx
│ │ └── index.ts
│ └── layout/ # Layout components
│ ├── Header.tsx
│ └── Footer.tsx
├── hooks/ # Custom hooks
│ ├── useTodos.ts
│ └── useLocalStorage.ts
├── types/ # TypeScript types
│ └── todo.ts
└── utils/ # Utility functions
└── cn.ts # clsx + tailwind-merge