The as
prop is a pretty cool ReactJS pattern that lets you change the HTML element or React component returned by passing a prop. Before we begin, here's a simplified example:
// component with `as` prop
function ComponentName({ as = "p", ...props }) {
let Component = as;
return (
<Component {...props} />
);
};
// using component
<ComponentName>Hello!</ComponentName>
// <p>Hello!</p>
<ComponentName as="div">Hello!</ComponentName>
// <div>Hello!</div>
<ComponentName as={AnotherComponent} onClick={doSomething}>Hello!</ComponentName>
// <AnotherComponent onClick={doSomething}>Hello!</AnotherComponent>
I recently wrote a blog post about building a dropdown component with React. And I created a dropdown list component, that would usually be an unordered list.
In the post, I said something along the lines of "I'll make this a ul
by default and can override it with the as
prop".
In case you were wondering what the hell I was going on about ☝️ - this blog post is all about the as
prop.
What is the as
prop?
The as
prop is often used to change the default element returned from a component. For example, if a component usually returns a div
you could pass as="ul"
to return a list. And it doesn't have to be an HTML element - you can also pass in a custom component like as={MyCustomDiv}
.
The as
prop isn't a special ReactJS prop or anything like that. It's the same as any other prop. You define what the as
prop is. But it's become the prop name of choice for dynamic component names.
So the code above won't work out of the box, but it's easy to implement, which I will show you shortly.
First, let me explain why you might use it.
The use case for dynamic component names
Suppose you have a component that will usually be a button
. But sometimes you want it to be a link - an a
tag, or if you are using React Router, the Link
component.
Or maybe you have a List
component, with all of your custom styles and settings for lists. And you can pass your list as an array and everything is perfect. And most of your lists are unordered, so it returns an ul
element. Great!
function List({ items, ...props }) {
return (
<ul className="m-10 text-gray-700">
{ items.map((item) => {
return <li className="p-3">{item}</li>
})}
</ul>
);
};
But what if sometimes you want an ordered list (ol
).
You don't want to duplicate the whole component for an ordered list, just change the element type.
Maybe you try something like:
// 🚨 this won't work
function List({ items, type, ...props }) {
return (
<{type} className="m-10 text-gray-700">
{ items.map((item) => {
return <li className="p-3">{item}</li>
})}
</{type}>
);
};
But the as
prop will work great for this!
Setting up the as
prop
Again, you don't have to call this prop as
. You can call it component
or element
or anything you like.
I like as
.
It's short, and it gets to the point!
Plus the as
prop is fairly commonly used, so other people using/reading your code are more likely to understand it.
The first thing we do is get as
from the props received and assign "ul" as the default function List({ as = "ul", items, ...props })
. as
will be optional, so we want a default value so the component continues to return the ul
element if we don't ask for anything different.
And then we assign as
to a Component
* variable.
let Component = as; // capital C on Component variable
That might seem pretty odd, but React components (anything that's not an HTML tag) have to be capitalised.
And since as
isn't an HTML tag (it's a variable) - we need a capital letter at the start.
And now we can return Component
instead of ul
as our element. And by default, it will return ul
- but, it can now be overridden!
function List({ as = "ul", items, ...props }) {
let Component = as; // capital C on Component variable
return (
<Component className="m-10 text-gray-700">
{ items.map((item) => {
return <li className="p-3">{item}</li>
})}
</Component>
);
};
*It doesn't need to be called Component
! let As = as;
or let Type = as;
would work too!
Using the as
prop
Now you have the as
prop set up in your component, you can use it! Just pass your element or component name.
<List as="ol" items={[ "First Item", "Second Item", "Third Item", "Forth Item", "Fifth Item" ]} />
// <ol>[...]</ol>
Since "ul" is the default, your unordered lists will still work the same as before.
<List items={[ "An Item", "Another Item", "Other Item", "Item X" ]} />
// <ul>[...]</ul>
And we are done! It's that simple!
More as
prop examples
I've used the as
prop in a few ways.
button
vs Link
The button
vs Link
situation springs to mind. Let's say you have a Card
component with a CardButton
that you always want styled the same. Maybe it's got an icon or a special transition effect, or it's big and bold and beautiful.
Most of the time it will be a button. It looks like the button, it acts like a button - it is a button.
But sometimes, that button needs to be an a
element. You want to direct the user somewhere else. It doesn't need to be a button. But, it's still a call to action, and you still want it to look like a button.
Do you duplicate the CardButton
component to create a CardLink
component? Replicating the design and just changing the button
to a
(or if you are using React Router - Link
).
Now if you make changes to the styles you need to remember to update both components the same, which doesn't sound very appealing.
Instead, let's just use the as
prop!
function CardButton({ as = "button", children, ...props }) {
let Component = as;
return (
<Component className="m-10 text-gray-700 ..." {...props}>
{children}
</Component>
);
};
And now we can use it:
// as a button (default)
<CardButton onClick={doSomething}>Do The Thing</CardButton>
// as an "a" element
<CardButton as="a" href="/somewhere">Go To The Thing</CardButton>
// as a `Link` component
<CardButton as={Link} to="/somewhere">Go To The Thing</CardButton>
And those onClick
or to
props?
We pass all the remaining {...props}
to the Component
, so they end up on the returned component and work just fine!
// passing props through to the returned component
<Component className="..." {...props}>
Custom components
The as
prop works with custom components, but instead of passing a string like as="button
or as="ul"
, you will pass an object, as={ComponentName}
or as={Link}
.
// as a `Link` component
<CardButton as={Link} to="/somewhere">Go To The Thing</CardButton>
// as a custom component
<CardButton as={MyCoolComponent} to="/somewhere">Go To The Thing</CardButton>
The setup all stays the same in the component that accepts the as
prop, you just need to remember that you are passing a variable so it's kind of like passing a function to your component as a prop.