with one click
hex-core-anti-patterns
// Top mistakes to avoid with hex-core. Load when writing React 19, editing components/ui/, handling forms, composing dialogs, or anywhere an agent is about to make a known-bad choice.
// Top mistakes to avoid with hex-core. Load when writing React 19, editing components/ui/, handling forms, composing dialogs, or anywhere an agent is about to make a known-bad choice.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | hex-core-anti-patterns |
| description | Top mistakes to avoid with hex-core. Load when writing React 19, editing components/ui/, handling forms, composing dialogs, or anywhere an agent is about to make a known-bad choice. |
These are the repeated mistakes across real hex-core projects. Most come from the .ai.commonMistakes field on the underlying components; this skill aggregates and contextualizes them.
forwardRef to new componentsReact 19 makes ref a regular prop. New hex-core components omit forwardRef. Legacy code keeping it is fine during migration, but don't add it to new code.
// ❌ WRONG (React 19)
const MyCard = forwardRef<HTMLDivElement, Props>((props, ref) => <div ref={ref} {...props} />);
// ✅ RIGHT
function MyCard({ ref, ...props }: Props & { ref?: Ref<HTMLDivElement> }) {
return <div ref={ref} {...props} />;
}
"use client" at the page levelPush the client boundary as deep as possible. A Server Component page can render a client <Demo /> without itself being client. If only the search input needs useState, mark only the search input.
// ❌ WRONG
"use client";
export default function Page() { return <div>...with one button that uses onClick</div>; }
// ✅ RIGHT
export default function Page() { return <div><ClientButton /></div>; } // ClientButton has "use client"
params and searchParams are async in Next 16// ❌ WRONG (Next 15 and earlier)
export default function Page({ params }: { params: { slug: string } }) { const { slug } = params; }
// ✅ RIGHT (Next 16)
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
}
// ❌ WRONG — grows unbounded
<Card header="Title" headerIcon={icon} showDivider={true} footer={...} />
// ✅ RIGHT — compose with children
<Card>
<CardHeader><CardTitle>Title</CardTitle></CardHeader>
<Separator />
<CardContent>...</CardContent>
<CardFooter>...</CardFooter>
</Card>
asChild// ❌ WRONG
<Button asChild>
<a href="/link">
<span onClick={handleClick}>...</span> {/* two click handlers, one wins */}
</a>
</Button>
// ✅ RIGHT — one interactive element
<Button asChild>
<a href="/link">Label</a>
</Button>
destructive variant is for actually destructive actionsRed buttons signal danger. Using variant="destructive" for a generic "Submit" destroys the signal. Reserve for delete, archive, drop, end-session.
{...field} into FormControl// ❌ WRONG — value/onChange not wired
<FormField name="email" render={({ field }) => (
<FormItem><FormControl><Input /></FormControl></FormItem>
)} />
// ✅ RIGHT
<FormField name="email" render={({ field }) => (
<FormItem><FormControl><Input {...field} /></FormControl></FormItem>
)} />
const form = useForm<LoginFields>({
resolver: zodResolver(loginSchema), // ← don't omit
defaultValues: {...}
});
FormLabel's htmlFor is wired through FormItem context. Outside FormItem, it loses the control id.
role="alertdialog".Using Dialog for a delete-confirm is semantically wrong — screen readers won't announce urgency.
Select has no search. Combobox is Command + Popover with search. Cross the threshold and switch.
DataTable implies sort + filter + pagination. A 10-item read-only list is just a <ul>. Don't pay the complexity cost for under 20 rows.
packages/components/ from consumer codeThe copy-the-code model means you own the components/ui/ files in your project. Edit those. Don't monkey-patch the upstream package.
verify_checklist after a non-recipe installIf you installed component-by-component via MCP get_component, the agent doesn't automatically know about internal dep chains. Run verify_checklist as the final step to catch combobox without command.