Skip to content
tezvyn:
Menu

The polymorphic 'as' prop pattern

Source: interviewHardHow cards are made

Summary

Building reusable components with correct typing and ref forwarding.

Key points

render the element from an as prop, infer props from the chosen element, and forward refs.

Watch out for

ignoring types or dropping the ref.

What's really being asked

This question probes deep component reuse: can you build one styled component that adapts its underlying element without duplicating logic, while preserving semantics, accessibility, prop correctness, and ref forwarding?

The full answer

The core idea is an as prop. The component reads as, defaults it to a sensible element such as button, and renders that element with the remaining props spread onto it. Use forwardRef so the consumer ref reaches the actual rendered node, not a wrapper. The hard part is typing. In TypeScript you make the component generic over the element type, then compute its valid props with ComponentPropsWithoutRef of that type, and merge them with your own props while omitting collisions. This way as equals a yields href and target, as equals button yields type and disabled, and a router Link yields its to prop, all checked at compile time.

The mistakes people make

Hardcoding a single element defeats the purpose. Forgetting forwardRef breaks focus management and integrations that need the node. Typing the props bag as any throws away the safety that makes polymorphism worthwhile. Spreading unknown props without merging className or style can silently clobber the component styling.

What usually comes next

How do you preserve a default element while still allowing override? How do you handle the ref type changing with as? What are the performance and bundle costs of the generic types? When should you prefer composition or asChild slot patterns instead, as Radix does with Slot?

A concrete example

A design system Button is normally a button, but a call to action in a card must navigate. Rendering Button with as equal to the router Link and a to prop produces an anchor with the visual button styling, keyboard and focus behavior intact, the ref forwarded for programmatic focus, and to type-checked because the props were inferred from Link.

Interview question

When implementing a polymorphic component via an as prop in TypeScript, what is the main reason to make the props generic over the rendered element type?

  • a.It lets valid element-specific props like href or type be type-checkedCorrect
  • b.It automatically forwards refs without needing forwardRef
  • c.It reduces the final JavaScript bundle size at runtime
  • d.It prevents the component from rerendering unnecessarily
Why?

Generic element typing infers the correct props for each as value, so href is valid for an anchor and type for a button. It is purely compile-time and does not affect bundle size or ref forwarding, which still requires forwardRef.

Just read this? Test yourself on what you have been reading.

Read the original → components.build

Put your scrolling time to good use

Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.

The iPhone app is on the way

We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.

Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.

Get it on Google PlayiPhone app coming soon

We are hiring for this. Open roles that interview on react — each one lists the topics its interview covers.

See open roles