Polymorphic rendering with asChild
When asChild is true, Button renders its child element instead of a native button. This lets you use Button's behavior with any element — a link, a div, or a custom component.
1<Button asChild>2 <a href="/dashboard">Go to Dashboard</a>3</Button>
The child element inherits all Button props: onClick, disabled, aria attributes, and keyboard handling.
Overview
Polymorphic rendering lets a component render as any HTML element or React component while keeping its behavior and styles. Flint UI uses the asChild pattern (popularized by Radix UI) instead of the traditional as prop.
Why asChild over as prop
The as prop approach (<Button as="a">) has TypeScript issues: props from the target element leak into the component's type, and ref forwarding gets messy. asChild avoids this by merging props onto the child element instead.
1// With asChild — clean, type-safe2<Button asChild>3 <a href="/dashboard">Go to Dashboard</a>4</Button>56// Renders: <a href="/dashboard" class="flint-button">Go to Dashboard</a>
How It Works
When asChild is true, the component does not render its default element. Instead, it clones the single child element and merges its own props (className, event handlers, aria attributes, ref) onto that child.
1import { Button } from '@flint-ui/core'2import Link from 'next/link'34// Works with Next.js Link5<Button asChild tone="primary" size="lg">6 <Link href="/signup">Get Started</Link>7</Button>89// Works with React Router Link10<Button asChild variant="ghost">11 <RouterLink to="/settings">Settings</RouterLink>12</Button>1314// Works with plain HTML elements15<Button asChild>16 <a href="https://github.com/flint-ui" target="_blank" rel="noopener">17 View on GitHub18 </a>19</Button>
Rules
asChildexpects exactly one child element- The child must accept a
ref(so no raw strings or fragments) - Props are merged: component props win for event handlers (they compose), child props win for everything else
classNamevalues are concatenated, not replaced
Common Patterns
Navigation buttons
1<Button asChild variant="outline">2 <a href="/api/auth/login">Sign In</a>3</Button>
Icon-only triggers
1<Button asChild size="icon" variant="ghost">2 <button aria-label="Close" onClick={onClose}>3 <XIcon />4 </button>5</Button>
Card as link
1<Card asChild>2 <a href={post.url} className="block hover:shadow-md transition-shadow">3 <h3>{post.title}</h3>4 <p>{post.excerpt}</p>5 </a>6</Card>