Usage
Search the project
<p><UIIcon name="lucide:search" /> Search the project</p>
<UIButton variant="outline">
<UIIcon name="lucide:trash-2" label="Delete" />
</UIButton>
UIIcon
writes an inline
<svg>. Each literal name is resolved during the build with the collection package installed in the project: there are no runtime requests or CDN, and the bundle only carries the icons you use.
Installation and collections
You need
@meekojs/meeko
>=1.8 <2, with modules registered by name. Install the module and each Iconify collection you want to use as
@iconify-json/<collection>. The module is registered by its name and its options go in the
icon
key, typed in
meeko.config.ts.
// pnpm add @meekojs/icon && pnpm add -D @iconify-json/lucide
// meeko.config.ts
export default defineMeekoConfig({
modules: ['@meekojs/icon'],
icon: {
collections: ['lucide'], // @iconify-json/lucide of the project
local: 'app/icons', // your own SVG files: 'local:<name>'
size: '1em',
include: ['lucide:sun', 'lucide:moon'], // for bind:name
},
})
The available names form a TypeScript union that Meeko generates in
.meeko/types: the editor completes
name="lucide:…", and a name that does not exist is a
meeko check
error with the closest suggestion. If
icon.collections
names a collection that is not installed,
meeko check,
meeko dev
and
meeko build
fail with the command to install it.
Dynamic names
With
bind:name, the literals of the expression are resolved at build time just like a fixed name. A value that does not appear in the code, such as one that comes from data, only works if it is in
icon.include
(or with
'collection:*', which includes the whole collection). Otherwise an empty
<svg>
is rendered and
meeko dev
warns in the console.
<!-- Literals of the expression: they are resolved at compile time -->
<UIIcon
bind:name="open.value ? 'lucide:x' : 'lucide:menu'"
bind:label="open.value ? 'Close menu' : 'Open menu'" />
<!-- A value that comes from data: the icon must be in icon.include -->
<UIIcon bind:name="mode.value" />How it is resolved
- Literal names.
During the build, a Vite plugin resolves each
name="…"of<UIIcon>with@iconify/utilsand inlines it in the component that uses it, before Meeko compiles it. The bundle of each page only carries its icons; SSR and the browser see the same SVG, so hydration and navigation do not flicker. - A name that does not exist.
It is a type error (
TS2820, with the closest suggestion) inmeeko check,meeko buildand the editor; the build also rejects it with the file and the position.
Types
The module generates the union of the available names in
.meeko/types/modules/%40meekojs%2Ficon/, one declaration per collection plus that of the local SVGs.
name
of
<UIIcon>
and
icon.include
of
meeko.config.ts
use that union. With Lucide (2,302 names), Tabler and Material Design Icons at once (22,802), TypeScript 7 checking grows by about 35 ms.
import type { IconName } from '@meekojs/icon'
const close: IconName = 'lucide:x'Your own SVGs
Each file in
app/icons
(or in the folder of
icon.local) is
local:<name>, in kebab-case. It is sanitized at build time with a list of allowed elements and attributes:
<foreignObject>,
<image>, links, animations,
class,
<script>,
<style>,
on*
handlers,
style
and every reference outside the icon (href
and
url(…)
only to
#id) are removed. Internal
ids are prefixed per icon so two icons do not clash, and a single-color icon switches to
currentColor.
<!-- app/icons/paw.svg -->
<UIIcon name="local:paw" label="Meeko" size="1.5em" />Accessibility, size and color
Without
label, the icon is decorative:
aria-hidden="true"
and
focusable="false". With
label, the
<svg>
gets
role="img"
and
aria-label; use it when the icon is the only content of a button or a link.
It is
1em
tall by default, so it follows the surrounding text, and it takes its color from
currentColor. Width, height,
viewBox
and fill are attributes, never inline styles: it works with a strict Content Security Policy. The module's stylesheet only aligns the icon with the baseline (.meeko-icon).
API
| Name | Type / default | Description |
|---|---|---|
name
|
IconName
| Icon: collection:icon or local:name. A literal is resolved at build time. |
label
|
string
| Accessible text. Without it, the icon is decorative. |
size
|
string
icon.size (1em)
| Height, a CSS length. The width follows the proportion of the icon. |
class
|
string
| Classes of the svg, which always carries meeko-icon. |
Module options
| Name | Type / default | Description |
|---|---|---|
collections
|
string[]
[]
| Iconify collections (lucide, mdi…); each one, the @iconify-json/<collection> package of the project. The editor completes the installed ones. |
include
|
IconInclude[]
[]
| Icons for dynamic names, or collection:* for a whole one. |
local
|
string | false
app/icons if it exists
| Folder of your own SVGs. |
size
|
string
1em
| Default height of the icons. |