BlogReactJS

The "as" prop in ReactJS and dynamic component names

Written by Codemzy on August 8th, 2023

Ever wanted to use dynamic component names or use a variable to say which type of component or element you need? Let me introduce you to the `as` prop!

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".

Usually, our dropdown menu will be a list, so let's make this a ul by default. We can always switch things up with the as prop if we want to later.

- Codemzy How to make a simple dropdown menu component in React

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.

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.