ワンクリックで
frontend
Frontend development rules and patterns for Next.js/React projects
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Frontend development rules and patterns for Next.js/React projects
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Creates or adds Convex to an app. Use for new Convex projects, npm create convex@latest, frontend setup, env vars, or the first npx convex dev run.
Builds reusable Convex components with isolated tables and app-facing APIs. Use for new components, reusable backend modules, integrations, or component boundary work.
Audits Convex performance for reads, subscriptions, write contention, and function limits. Use for slow features, insights findings, OCC conflicts, or read amplification.
Sets up Convex auth, identity mapping, and access control. Use for login, auth providers, users tables, protected functions, or roles in a Convex app.
| name | frontend |
| description | Frontend development rules and patterns for Next.js/React projects |
components/components/ui/components/[feature]/Dialog → Integrated → Form:
FooDialog.tsx - Dialog wrapper with trigger buttonFooIntegrated.tsx - Form logic + submission handlingFooForm.tsx - Pure form with react-hook-form + zod validationhooks/cn helper for conditional and dynamic classname concatenationconst formatPrice = (price: number) =>
new Intl.NumberFormat("en-NG", {
style: "currency",
currency: "NGN",
maximumFractionDigits: 0,
}).format(price);
Returns: "₦1,500"
// Query
const data = useQuery(api.myFunction, { arg });
// Mutation
const mutate = useMutation(api.myMutation);
mutate({ arg });
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
const schema = z.object({
name: z.string().min(1, "Required"),
});
const { register, handleSubmit } = useForm({
resolver: zodResolver(schema),
});
AlertDialog for destructive actionsDialog for formsTabs, TabsList, TabsTrigger, TabsContentInstead of repeated find() calls in JSX:
// Bad - repeated find in JSX
<Form initialData={currentPlanId ? plans.find(p => p.id === currentPlanId) : undefined} />
// Good - extract to variable
const currentPlan = plans.find(p => p.id === currentPlanId);
<Form initialData={currentPlan} />
const computed = React.useMemo(() => expensiveOperation(data), [data]);
Return early to reduce nesting:
if (!data) return <Skeleton />;
return ( /* main content */ );
Avoid creating separate state for derived values:
// Bad - duplicate state
const [filtered, setFiltered] = useState(data);
useEffect(() => setFiltered(data.filter(...)), [data]);
// Good - derive in render
const filtered = data.filter(...);
// Bad
<Component a={obj.a} b={obj.b} c={obj.c} d={obj.d} />
// Good
const { a, b, c, d } = obj;
<Component a={a} b={b} c={c} d={d} />
// Or as spread
<Component {...pick(obj, 'a', 'b', 'c', 'd')} />
Forms should use local isSubmitting state passed to isLoading prop:
const [isSubmitting, setIsSubmitting] = useState(false);
<Form
isLoading={isSubmitting}
onSubmit={async (values) => {
setIsSubmitting(true);
try {
await mutation(values);
} finally {
setIsSubmitting(false);
}
}}
/>
// Form button shows "Saving..." when isLoading=true
Inline functions cause child components to rerender on every parent render:
// Bad - creates new function each render
<Button onClick={() => handleClick(id)} />
// Good - use stable handler, pass id as argument
<Button onClick={handleClick} payload={id} />
Prefer map with direct access over multiple/nested ternary operations for JavaScript conditionals.
// Bad - nested ternaries
const value = var === "a" ? 1 : var === "b" ? 2 : var === "c" ? 3 : "default";
// Good - map lookup
const valueMap = {
"a": 1,
"b": 2,
"c": 3
};
const value = valueMap[var] || "default";
// Show/Hide with && (for no else case)
{isOpen && <Content />}
// Ternary for else case
{isOpen ? <OpenState /> : <ClosedState />}
Group related components for cleaner exports:
// Instead of
export { Button } from "./button";
export { ButtonGroup } from "./button-group";
// Use compound component
export const Button = Object.assign(ButtonRoot, { Group: ButtonGroup });
// Usage: <Button.Group>