Astro has dynamic tags — a way to take an element or component as a prop.
I had trouble finding the feature because the React community calls this pattern the "polymorphic as prop". Because we enjoy our pseudo-computer-science bullshit artisinally.
Dynamic tags are simple to implement in Astro.
No-fuss implementation
- Take a capitalized
Element
prop as a local variable. - Render that prop as a template tag.
- Take and spread rest-props.
- Render children using Astro's un-named
<slot />
.
---
const { Element, ...props } = Astro.props;
---
<Element {...props}><slot /></Element>
Destructure and rename the element prop (for convenience or convention)
Uppercase prop names can look out of place in templates. Destructure and rename the Element
prop in one to provide a more ergonomic/conventional authoring experience.
---
const { as: Element, ...props } = Astro.props;
---
<Element {...props}><slot /></Element>
Astro templates require that dynamic tags be capitalized. Renaming the element prop is a convent way to follow that requirement while providing a more conventional API to consumers.
Accept and de-duplicate classes with class:list
Astro has a class:list
directive for orchestrating dynamic classes. Provide the class:list
directive an array with both provided and component classes.
class:list
is smart and automatically removes duplicate classes.
---
const {
as: Element = "div",
class: providedProps,
...props
} = Astro.props;
const componentClasses = "prose prose-slate dark:prose-invert";
---
<Element {...props} class:list={[componentClasses, providedProps]}>
<slot />
</Element>
Note: class
needs to be renamed when destructured because values can not be assigned to reserved words.
Complete with TypeScript interface
This is my completed component, with the TypeScript interface.
Yours needs will likely vary.
---
interface Props {
as?: "body" | "main" | "article";
class?: "string";
}
const {
as: Element = "div",
class: providedProps,
...props
} = Astro.props;
---
<Element {...props} class:list={[componentClasses, providedProps]}>
<slot />
</Element>
BEWARE: dynamic tags don't honor hydration client directives
Astro provides client directives for hydrating client-side UI. Those don't work with dynamic tags.
If you're using dynamic tags for static layouts — like me — this isn't an issue.
Takeaways
Astro supports the "polymorphic as prop" pattern popular in React. And the additional standard tooling of TypeScript and class:list
directive make it even easier to consistently implement.