#Dynamically importing icons as defined in content file

10 messages · Page 1 of 1 (latest)

runic ibex
#

Here's a sample of a content file in Markdown:

---
title: "Title here"
tools: ["Typescript", "Svelte", "Tailwindcss", "Rust"]
---

And here's a sample of my viewer component:

---
import { type CollectionEntry } from "astro:content";
import { Image } from "astro:assets";
import Card from "../ui/card.astro";

const { project } = Astro.props as { project: CollectionEntry<"projects"> };
const image = project.data.smallTileImage || project.data.mainImage;
---

<h3 class="text-xl font-semibold tracking-tight">
    {project.data.title}
</h3>

I would like to import the icons (as defined by the tools array) from the simple-icons-astro package and display them in the viewer component. How can I do this?

last walrusBOT
#

Hi @runic ibex. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To dynamically import icons from the simple-icons-astro package based on the tools array defined in your Markdown content file, you can use Astro's dynamic import feature. Here's how you can modify your viewer component to achieve this:

---
import { type CollectionEntry } from "astro:content";
import { Image } from "astro:assets";
import Card from "../ui/card.astro";

const { project } = Astro.props as { project: CollectionEntry<"projects"> };
const image = project.data.smallTileImage || project.data.mainImage;

// Dynamically import icons
const icons = await Promise.all(
  project.data.tools.map(async (tool) => {
    const iconName = tool.toLowerCase().replace(/\s+/g, '');
    try {
      const icon = await import(`simple-icons-astro/icons/${iconName}.js`);
      return icon.default;
    } catch (e) {
      console.warn(`Icon not found for ${tool}`);
      return null;
    }
  })
);
---

<h3 class="text-xl font-semibold tracking-tight">
    {project.data.title}
</h3>

<div class="flex gap-2 mt-2">
  {icons.map((Icon) => Icon && <Icon size={24} />)}
</div>

This solution uses the simple-icons-astro package as requested. Here's a breakdown of what's happening:

  1. We use Promise.all with map to create an array of promises, each attempting to import an icon based on the tool name.
  2. The tool name is converted to lowercase and spaces are removed to match the expected icon file names.
  3. We use a try-catch block to handle cases where an icon might not exist for a particular tool.
  4. In the template section, we render the icons that were successfully imported.

Note that this approach assumes that the icon names in the simple-icons-astro package match the tool names (after converting to lowercase and removing spaces). You might need to adjust the iconName transformation if the package uses different naming conventions.

#

Also, make sure you have installed the simple-icons-astro package in your project:

npm install simple-icons-astro

This solution is based on the information provided in the Simple Icons Astro documentation and Astro's dynamic import capabilities as described in the Astro documentation on imports.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@runic ibex Kapa.ai is still learning and improving, please let me know how I did by reacting below.

runic ibex
#

here's an example of how it's imported

import Rust from "simple-icons-astro/Rust"
``` @last walrus
#

here's an example of how it's imported

import Rust from "simple-icons-astro/Rust"
``` @last walrus
last walrusBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Thank you for providing that additional information about how the icons are imported from the simple-icons-astro package. Based on this, we can adjust the dynamic import approach to match the correct import syntax. Here's an updated version of the viewer component that should work with the simple-icons-astro package:

---
import { type CollectionEntry } from "astro:content";
import { Image } from "astro:assets";
import Card from "../ui/card.astro";

const { project } = Astro.props as { project: CollectionEntry<"projects"> };
const image = project.data.smallTileImage || project.data.mainImage;

// Dynamically import icons
const icons = await Promise.all(
  project.data.tools.map(async (tool) => {
    try {
      const icon = await import(`simple-icons-astro/${tool}`);
      return icon.default;
    } catch (e) {
      console.warn(`Icon not found for ${tool}`);
      return null;
    }
  })
);
---

<h3 class="text-xl font-semibold tracking-tight">
    {project.data.title}
</h3>

<div class="flex gap-2 mt-2">
  {icons.map((Icon) => Icon && <Icon size={24} />)}
</div>

In this updated version:

  1. We've simplified the dynamic import statement to directly use the tool name as it appears in the tools array. This matches the import syntax you provided: import Rust from "simple-icons-astro/Rust".

  2. We're still using Promise.all with map to create an array of promises, each attempting to import an icon based on the tool name.

  3. The try-catch block remains to handle cases where an icon might not exist for a particular tool.

  4. The rendering of the icons in the template section remains the same.

#

This approach assumes that the names in your tools array exactly match the icon names in the simple-icons-astro package. For example, if your tools array contains "Typescript", "Svelte", "Tailwindcss", and "Rust", it should work correctly.

If you find that some icon names don't match exactly (e.g., if "Tailwindcss" should be "TailwindCSS" in the import), you might need to add a mapping function to transform the tool names to their correct import names.

Astro Documentation on Imports provides more information on dynamic imports if you need to further customize this approach.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: