Skip to content

Icons ​

The library renders icons through a single Icon component, backed by Lucide. You pass a lucide icon name; the component resolves it to a component at runtime.

Usage ​

vue
<script setup lang="ts">
import { Icon } from '@myghf/ui'
</script>

<template>
  <Icon name="heart" />
  <Icon name="chart-line" :size="20" />
  <Icon name="calendar" class="text-muted" />
</template>
PropTypeDefaultNotes
namestring— (required)A lucide icon name; see naming below
sizenumber16Passed to the underlying lucide component
  • heartBrand / cardiology
  • chart-lineAnalytics
  • calendarAppointments
  • stethoscopeClinical
vue
<script setup lang="ts">
import { Icon } from '@myghf/ui'

const icons = [
  { name: 'heart', usage: 'Brand / cardiology' },
  { name: 'chart-line', usage: 'Analytics' },
  { name: 'calendar', usage: 'Appointments' },
  { name: 'stethoscope', usage: 'Clinical' },
]
</script>

<template>
  <ul class="grid gap-3 sm:grid-cols-2">
    <li
      v-for="item in icons"
      :key="item.name"
      class="flex items-center gap-3 rounded-lg border border-border bg-surface p-3"
    >
      <Icon :name="item.name" class="size-5 text-primary-500" />
      <span class="text-sm text-foreground">
        <code class="text-xs">{{ item.name }}</code>
        <span class="block text-xs text-muted">{{ item.usage }}</span>
      </span>
    </li>
  </ul>
</template>

Lucide naming ​

name is normalized before lookup, so several spellings resolve to the same icon:

You passResolved to
heartHeart
chart-lineChartLine
chartLineChartLine
ChevronDownChevronDown

Under the hood the component runs the name through resolveIconName (kebab-case normalization) and then toPascalCase before indexing the lucide-vue-next exports.

Guidance:

  • Prefer kebab-case (chart-line, arrow-right) — it matches Lucide's own naming and reads consistently with Tailwind class names.
  • No prefixes or suffixes. Write heart, not lucide-heart or HeartIcon.
  • Unknown names fall back to a circle rather than rendering nothing, so a gap is visible instead of silent. If you see a circle, the name is wrong or the icon does not exist in the bundled Lucide version.
  • An empty name also falls back to a circle.

Browse names in the Lucide icon library. The library resolves against lucide-vue-next ^0.525.0; icons added to Lucide after that release are not available through Icon until the dependency is bumped.

Sizing and color ​

  • size sets the pixel dimensions; default is 16.
  • Icons render with stroke="currentColor", so colour comes from text colour: text-muted, text-primary-500, text-foreground. Never hard-code a hex value.
  • Inside Button, the base styles apply [&_svg]:size-4, so icons are 16 px regardless of the size prop. Use the button's size variants to scale the whole control.
  • Use a Tailwind size-* class (class="size-5") when you need a one-off size in markup.

Accessibility ​

Icon renders aria-hidden="true" — it is always decorative. That means:

  • Icon-only controls need a name on the control, not the icon:
    vue
    <Button size="icon" aria-label="Search">
      <Icon name="search" />
    </Button>
  • Icon + text pairs are fine as-is, because the text supplies the name.
  • Do not rely on colour or shape alone to convey meaning; pair the icon with a label or tooltip text.

Using Lucide directly ​

Components like Password import lucide components directly for their own chrome. If you want to render a Lucide icon outside the Icon component (for example, in a slot that expects a component), install lucide-vue-next in your own app rather than relying on the library's transitive dependency:

bash
npm install lucide-vue-next
vue
<script setup lang="ts">
import { HeartPulse } from 'lucide-vue-next'
</script>

<template>
  <HeartPulse class="size-5 text-error-500" />
</template>
  • The Icon component API is documented on the components pages.
  • The resolveIconName and toPascalCase helpers are documented on the utilities pages.
  • This page covers icon usage guidance only.

Released under the MIT License.