Agent skill
scaffold-context
Scaffold a new React Context module inside an existing feature using the Context + useReducer pattern (state, actions, reducer, context, provider, and barrel exports).
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/scaffold-context
SKILL.md
Scaffold Context
Scaffold a new React Context module within an existing feature, following the Context + useReducer pattern.
Usage
/scaffold-context <context-name>
Example: /scaffold-context voice-recorder
Instructions
Follow these steps to scaffold a new context:
Step 1: Parse Context Name
The context name is provided in $ARGUMENTS. If $ARGUMENTS is empty or missing, use AskUserQuestion to prompt:
What is the name of the context? (use kebab-case, e.g., voice-recorder)
Step 2: Validate Context Name
Ensure the context name:
- Uses kebab-case (lowercase letters and hyphens only)
- Does not start or end with a hyphen
- Is not empty
If invalid, inform the user and ask for a valid name.
Step 3: Select Target Feature
List all existing features by scanning features/ directory.
If no features exist, inform the user they need to create a feature first using /scaffold-feature.
If only one feature exists, confirm with the user that they want to add the context to that feature.
If multiple features exist, use AskUserQuestion to ask which feature to add the context to:
Question: "Which feature should this context belong to?" Header: "Feature" Options: List existing feature names (up to 4). If more than 4 features exist, show the 4 most recently modified and include guidance to specify "Other" for unlisted features.
Step 4: Ensure Contexts Directory Exists
Check if features/{feature-name}/contexts/ directory exists. If not, create it along with an index.ts barrel file:
contexts/index.ts:
// {Feature Name} Contexts
//
// Add your React Context modules here. Each context should have its own directory:
//
// contexts/
// └── {context-name}/
// ├── {context-name}-state.tsx - State interface and initial values
// ├── {context-name}-actions.tsx - Action types and interfaces
// ├── {context-name}-reducer.tsx - Reducer function
// ├── {context-name}-context.tsx - React Context creation
// └── {context-name}-provider.tsx - Provider component
Step 5: Generate Context Files
Create the context directory at features/{feature-name}/contexts/{context-name}/.
Generate the following 6 files:
{context-name}-state.tsx
export interface {ContextName}State {
// Add your state properties here
isLoading: boolean;
error?: string;
}
export const {contextName}InitialState: {ContextName}State = {
isLoading: false,
error: undefined,
};
{context-name}-actions.tsx
export enum {ContextName}ActionType {
setLoading = "SET_LOADING",
setError = "SET_ERROR",
reset = "RESET",
}
export interface SetLoadingAction {
type: {ContextName}ActionType.setLoading;
payload: boolean;
}
export interface SetErrorAction {
type: {ContextName}ActionType.setError;
payload: string | undefined;
}
export interface ResetAction {
type: {ContextName}ActionType.reset;
}
export type {ContextName}Action =
| SetLoadingAction
| SetErrorAction
| ResetAction;
{context-name}-reducer.tsx
import { type {ContextName}State, {contextName}InitialState } from "./{context-name}-state";
import { type {ContextName}Action, {ContextName}ActionType } from "./{context-name}-actions";
export function {contextName}Reducer(
state: {ContextName}State,
action: {ContextName}Action
): {ContextName}State {
switch (action.type) {
case {ContextName}ActionType.setLoading:
return {
...state,
isLoading: action.payload,
};
case {ContextName}ActionType.setError:
return {
...state,
error: action.payload,
isLoading: false,
};
case {ContextName}ActionType.reset:
return {contextName}InitialState;
default:
return state;
}
}
{context-name}-context.tsx
import { createContext, type Dispatch } from "react";
import { type {ContextName}State, {contextName}InitialState } from "./{context-name}-state";
import { type {ContextName}Action } from "./{context-name}-actions";
export const {ContextName}Context = createContext<{
state: {ContextName}State;
dispatch: Dispatch<{ContextName}Action>;
}>({
state: {contextName}InitialState,
dispatch: () => {},
});
{context-name}-provider.tsx
"use client";
import { useReducer, type ReactNode } from "react";
import { {ContextName}Context } from "./{context-name}-context";
import { {contextName}Reducer } from "./{context-name}-reducer";
import { type {ContextName}State, {contextName}InitialState } from "./{context-name}-state";
export interface {ContextName}ProviderProps {
children: ReactNode;
initialState?: Partial<{ContextName}State>;
}
export function {ContextName}Provider(props: {ContextName}ProviderProps) {
const { children, initialState } = props;
const [state, dispatch] = useReducer(
{contextName}Reducer,
{ ...{contextName}InitialState, ...initialState }
);
return (
<{ContextName}Context.Provider value={{ state, dispatch }}>
{children}
</{ContextName}Context.Provider>
);
}
index.ts
export { {ContextName}Context } from "./{context-name}-context";
export { {ContextName}Provider } from "./{context-name}-provider";
export type { {ContextName}ProviderProps } from "./{context-name}-provider";
export { {contextName}Reducer } from "./{context-name}-reducer";
export { type {ContextName}State, {contextName}InitialState } from "./{context-name}-state";
export {
{ContextName}ActionType,
type {ContextName}Action,
type SetLoadingAction,
type SetErrorAction,
type ResetAction,
} from "./{context-name}-actions";
Step 6: Output Summary
After creating all files, output a summary:
Created context: {context-name} in {feature-name}
features/{feature-name}/contexts/{context-name}/
├── {context-name}-state.tsx
├── {context-name}-actions.tsx
├── {context-name}-reducer.tsx
├── {context-name}-context.tsx
├── {context-name}-provider.tsx
└── index.ts
Next steps:
1. Define your state properties in {context-name}-state.tsx
2. Add action types and interfaces in {context-name}-actions.tsx
3. Implement state transitions in {context-name}-reducer.tsx
4. Create a custom hook in hooks/ for convenient context access:
// hooks/use-{context-name}.ts
export function use{ContextName}() {
const { state, dispatch } = useContext({ContextName}Context);
// Add helper methods that wrap dispatch calls
return { ...state, /* methods */ };
}
Naming Conventions
- context-name: kebab-case (e.g.,
voice-recorder) - ContextName: PascalCase (e.g.,
VoiceRecorder) - contextName: camelCase (e.g.,
voiceRecorder)
Convert kebab-case to other cases:
| Input (kebab) | PascalCase | camelCase |
|---|---|---|
voice-recorder |
VoiceRecorder |
voiceRecorder |
user-auth |
UserAuth |
userAuth |
Conversion rules:
- Split on hyphens
- For PascalCase: capitalize first letter of each word, join without separators
- For camelCase: lowercase first word, capitalize first letter of subsequent words, join without separators
Didn't find tool you were looking for?