0

I'm trying to dynamically load only specific icons from the lucide-vue-next package in my vue page, however it's not wanting to work. I'm using Laravel 12 (Latest), Vue3 (Latest shipped with Laravel), and Inertia.js (Latest shipped with Laravel)

My controller code:

$races = Race::with('skills', 'trait')->get();
$raceIcons = 'Sparkles'; //implode(',', $races->pluck('icon')->toArray());
return Inertia::render('game/character/Create', ['races' => $races, 'races_icons' => $raceIcons]);

My vue code:

<script setup lang="ts">
import { ref, computed } from 'vue';
import { Head, useForm, usePage } from '@inertiajs/vue3';

const page = usePage();
const raceIcons = computed(() => page.props.races_icons); // With or without .value at the end of the statement before the ;
console.log(raceIcons);
...
</script>

<script lang="ts">
    import { raceIcons } from 'lucide-vue-next';
</script>

I'm getting the error: Uncaught ReferenceError: raceIcons is not defined

The component isn't loaded when I try to do this dynamically, however when I call:

import { Sparkles } from 'lucide-vue-next';

It works just fine. Is there any way I can get this to work? I really don't want to load absolutely every single lucide icon on every page.

2
  • Apologies, I'll try to clear things up. I have to <script> tags purely to test. The third component is just an example of what works - I can import specific icons when naming them manually. raceIcons is provided by the Inertia backend. Commented yesterday
  • This can be XY problem. For dynamic import name, you could use (await import('lucide-vue-next'))[page.props.races_icons]. But this will disable tree shaking and forcedly include the whole library Commented 23 hours ago

1 Answer 1

1

Yeah, that’s not how imports work, and Lucide isn’t psychic.

<script setup lang="ts">
import { computed } from 'vue';
import { usePage } from '@inertiajs/vue3';
import { Sparkles, Sword, Shield } from 'lucide-vue-next'; // only what you actually use

const page = usePage();

// e.g. "Sparkles", "Sword", etc. coming from Laravel
const iconName = computed(() => page.props.races_icons as string);

const iconsMap = {
  Sparkles,
  Sword,
  Shield,
} as const;

// pick the right component (fallback to Sparkles)
const CurrentIcon = computed(
  () => iconsMap[iconName.value as keyof typeof iconsMap] ?? Sparkles,
);
</script>

<template>
  <component :is="CurrentIcon" class="w-5 h-5" />
</template>

What went wrong in your version:

  • import { raceIcons } from 'lucide-vue-next' is looking for a named export literally called raceIcons. It does not care about your computed raceIcons string.

  • You also mixed <script setup> with a second <script> block and shadowed the same name, so the template never sees what you think it sees.

If you really want “any” icon by string and don’t care about bundle size, you can do:

import * as Lucide from 'lucide-vue-next';

const CurrentIcon = computed(
  () => (Lucide as any)[iconName.value] ?? Lucide.Sparkles,
);

But that pulls basically all icons into your bundle. The map approach is the sane one.

New contributor
Fazle Ryan is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.