paint-brush
Implementing Dynamic Tags in Astroby@chantastic
192 reads

Implementing Dynamic Tags in Astro

by chantasticAugust 31st, 2023
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Astro has a way to take an element or component as a prop. The React community calls this pattern the "polymorphic as prop" Dynamic tags are simple to implement in Astro. Astro provides client directives for hydrating client-side UI that don't work with dynamic tags.
featured image - Implementing Dynamic Tags in Astro
chantastic HackerNoon profile picture

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

  1. Take a capitalized Element prop as a local variable.
  2. Render that prop as a template tag.
  3. Take and spread rest-props.
  4. 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.


Also published here.