How to use dynamic components in Astro

September 12, 2025 · 4 min read

Astro is rapidly gaining traction as a framework for building high-performance websites. But if you’ve ever worked with React, Vue, or other component-based frameworks, shifting some of your habits into Astro might come with a few curveballs. One such curveball lands when you try to dynamically render components based on data, for instance, serving up different components based on a CMS field, or spinning out a social icon depending on a value.
This blog post will walk you step by step through “dynamic components” in Astro. Along the way, we’ll explore how things differ from more traditional React-style approaches, how you can set up a maintainable pattern, and what pitfalls to avoid. Let's make our Astro sites more dynamic, flexible, and less repetitive.
Rendering different components
Imagine you’re building a marketing site with Astro, and your content model from a headless CMS has a “sections” array. Each item can be of type Hero, Testimonial, FeatureGrid, CTA, etc. Naturally, you want your site to render different components depending on the type. Easy if you’re coming from React, just a big ol’ switch-case and you’re done.
But here’s the issue: Astro doesn’t let you just call <{getComponent(type)} /> directly like you might in React. Which leaves you scratching your head.
Dynamic components matter because they let you:
Reuse patterns without repetitive
if/elsechecksKeep your code clean and DRY (don't repeat yourself)
Scale with CMS-driven content or data-driven UI
If you’ve tried the brute-force method (many if checks), you’ve probably already felt the pain. Let’s explore that.
The React mindset vs. the Astro reality
In React development, we’d often do something like:
function renderIcon(name) {
switch (name) {
case 'Twitter':
return <TwitterIcon />;
case 'Facebook':
return <FacebookIcon />;
default:
return null;
}
}Then in our component:
<div>{renderIcon(socialName)}</div>React’s rendering model lets you treat components almost like functions, plug in variables, and the result is a nicely rendered element.
Astro, however, is designed differently: It’s a static site builder with islands of interactivity, and the compiler doesn’t like you passing components around quite the same way.
Using conditional statements
It’s tempting to start with the brute force approach:
{ name === 'X' ? <XIcon /> : null }
{ name === 'Github' ? <GithubIcon /> : null }
{ name === 'Linkedin' ? <LinkedinIcon /> : null }Yes, it works. No, it doesn’t scale. As soon as you add five more social media platforms, or worse, you let marketing pick any random platform tomorrow and your file turns into a “giant sandwich of spaghetti code.”
Scaling is the key issue, which brings us to maps.
Understanding Astro’s component rendering
Astro compiles its .astro files into HTML + JavaScript. Unlike JSX, you can’t directly treat a component identifier like a computed value (<SomeComponent /> vs <components[name] />). You need to explicitly resolve which component you want to render before Astro’s rendering pipeline kicks in.
That’s why the map pattern makes sense: instead of hardcoding checks, you rely on an object lookup table.
Using dynamic maps
The insight here is that components in Astro are just imports. Imports you can store in a dictionary. Dictionaries you can index with dynamic keys. That’s the big unlock.
Let's try it out with our example: the map of social icons.
---
import Twitter from "../assets/svg/twitter.svg";
import Instagram from "../assets/svg/instagram.svg";
import YouTube from "../assets/svg/youtube.svg";
import Linkedin from "../assets/svg/linkedin.svg";
const socialMap = {
Twitter,
Instagram,
YouTube,
Linkedin,
};
---Clean. Compact. And now, extensible. Want to add TikTok? Just import and extend the object.
Building the dynamic component
Let’s break down the full SocialMedia.astro component:
---
import Twitter from "../assets/svg/twitter.svg";
import Instagram from "../assets/svg/instagram.svg";
import YouTube from "../assets/svg/youtube.svg";
import Linkedin from "../assets/svg/linkedin.svg";
const socialMap = {
Twitter,
Instagram,
YouTube,
Linkedin,
};
type SocialMediaName = keyof typeof socialMap;
interface Props {
name: SocialMediaName;
}
const { name, ...rest } = Astro.props as Props;
const Template = socialMap[name] ?? Fragment;
---
<Template {...rest} />How it works
Grab the
nameprop (e.g.,"Twitter").Look it up in
socialMap.If found, return that component. If missing, return
null.Assign a fallback (
Fragment) to prevent the app from crashing.Render it dynamically with
<Template />.
Passing props dynamically
Check this part: <Template {...rest} />.
That ...rest spread means if you pass custom props to <SocialMedia />, they get forwarded down to the chosen icon component. Example:
<SocialMedia name="Twitter" class="hover:text-blue-500" width="32" height="32" />Those class, width, height props pass down unchanged to the <Twitter /> component. Future-proof and flexible.
Expanding the pattern to CMS content
Let's see a real-world use case. Your CMS gives you the following data:
{
"type": "FeatureGrid",
"props": { "features": [ ... ] }
}Following the same approach:
---
import Hero from "../components/Hero.astro";
import FeatureGrid from "../components/FeatureGrid.astro";
import Testimonial from "../components/Testimonial.astro";
const sectionComponents = {
Hero,
FeatureGrid,
Testimonial,
};
const { type, props } = Astro.props;
const Template = sectionComponents[type] ?? Fragment;
---
<Template {...props} />You might prefer having a function to check if the key exists, and if not, you can display a console.warn so developers know a section is missing.
Conclusion
Dynamic components in Astro may not feel as “automatic” as React, but once you master the map pattern, it’s actually not that difficult. Instead of a tangle of conditionals, you get clean, scalable, lookup-driven rendering. Whether you’re wiring social media icons or entire CMS-driven page sections, this pattern will make your Astro projects more maintainable, flexible, and enjoyable.
Bring Your Ideas to Life 🚀
If you need help with an Astro project, let's get in touch.
Lucky Media is proud to be recognized as a leading Astro partner agency.
FAQs
Can I use this dynamic component approach for interactive React/Vue components inside Astro?
You can import React or Vue components into Astro maps the same way. Just make sure you add appropriate client directives (client:load, client:visible, etc.), so they hydrate properly.
What happens if the key doesn’t exist in the map?
By default, our function returns null , and Astro renders nothing. This is equivalent to <></>.
Can I pull in hundreds of SVG icons this way?
Performance-wise, better to use import.meta.glob() and lazy-load only the ones you need, instead of importing them all up front.
Does this apply only to icons or CMS-driven data?
This pattern works wherever you’d normally consider a big conditional rendering tree: form inputs, themes, feature flags, page layouts, you name it.
Technologies

Stay up-to-date
Be updated with all news, products and tips we share!
On this page

