Skip to main content
Vuetify0 v1.0 is here
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

EmIcon

Edit this page
Report a Bug
Open issues
Copy Markdown

Renders elementIntermediateAug 14, 2026

Draws a named glyph from Emerald’s icon set. Icons are addressed by the role they play, not by the picture they show, and are decorative unless you say otherwise.

Usage

name is a role: what the icon is for, not what it depicts. You ask for settings and get the sliders drawing; you ask for mail and get the envelope. There is no file to import, no icon font to load, and no sprite sheet — the glyph is inline SVG drawn from a registry the plugin installs.

The set is deliberately small. 48 drawings answer to 72 names, because 24 of those names are aliases onto a shared drawing. That is a design decision rather than an economy: finance and payments point at the same card because they are the same concept in two dashboards, and giving them separate art would make the product look less coherent, not more.

<script setup lang="ts">
  import { EmIcon } from '@paper/emerald'
</script>

<template>
  <div class="emerald-docs-row">
    <EmIcon name="layout" />

    <EmIcon name="card" />

    <EmIcon name="envelope" />

    <EmIcon name="calendar" />

    <EmIcon name="search" />

    <EmIcon name="star" />
  </div>
</template>

<style>
  .emerald-docs-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--emerald-spacing-m, 16px);
    color: var(--emerald-on-surface);
  }
</style>

Anatomy

vue
<script setup lang="ts">
  import { EmIcon } from '@paper/emerald'
</script>

<template>
  <EmIcon name="calendar" />
</template>

Composed on v0

There is no v0 icon component; EmIcon is built on a v0 composable instead. The registry is a createTokens instance, and resolution is createTokens’ own alias dereferencing rather than anything Emerald wrote.

That is what makes the alias table free. createTokens resolves {role} references at read time, so an alias is stored as the literal string {envelope} and dereferenced when it is asked for. mail and envelope are two entries pointing at one array of path data, and adding your own is the same one-line shape.

The plugin is a createPluginContext trinity — createEmeraldIconsContext, createEmeraldIconsPlugin, useEmIcons — which is why namespace exists on the component: a subtree can be given its own registry, and EmIcon will resolve against that one instead. It also carries a fallback, so EmIcon draws correctly in an app with no Emerald plugin installed at all; the built-in set is built once on first use and shared from then on.

Examples

Sizes

size selects a step on the --emerald-icon-* scale: s is 18px, m 20px, l 24px, xl 32px. The prop sets a data-size attribute and the dimension arrives through CSS custom properties, so the icon scales with the token rather than with an inline style you would have to override.

Those rules are wrapped in :where(), which gives them zero specificity, and that is a feature. Any single-class rule on a host element outranks the size prop — which is exactly how EmSelect pins its caret to 16px without passing a prop down through the component. If you need an icon at a size the scale does not have, style the container; you do not need a new token or an inline override.

The glyphs are stroked, not filled, and stroke-width is its own custom property, so an icon enlarged past xl thins out proportionally instead of turning into a heavy blob.

Decorative or labelled

This is the only accessibility decision the component asks you to make, and it turns on the single label prop.

With no label, the icon renders aria-hidden="true" and no role — it is invisible to assistive technology. That is the right default and the common case: an icon beside its own text label, or inside a button that already has a name, is duplicate information, and announcing it makes the control read twice.

With a label, the icon becomes role="img" with that string as its accessible name, and the aria-hidden is dropped. Reach for it only when the icon is carrying meaning nothing else on screen carries — a status glyph in a table cell, a bare trend arrow beside a figure.

The third case in the example is the one that trips people up. For an icon-only button, the name belongs on the button, not the icon: the button is what gets focused and activated, so it is the thing that needs a name. Labelling the glyph instead leaves the control anonymous and produces a nested, doubly-announced name — pass ariaLabel to EmButton and leave the icon decorative.

Every canonical role in the set, drawn from emeraldIcons — the same map the registry is built from, exported from the package root. Iterating it is the honest way to build a picker: the gallery cannot drift from the artwork, because it is the artwork.

Note what this list is not. These are the 48 canonical roles only; the 24 aliases resolve into this set and are listed in the table below rather than drawn again here, because a gallery that showed mail and envelope as separate tiles would imply two drawings where there is one.

If a name does not resolve, EmIcon renders nothing at all and logs the unknown role in development. That is deliberate — a placeholder box would ship to production looking like a considered design choice, while an empty space reads as the bug it is.

Roles

The canonical set, grouped the way the source groups it.

GroupRoles
Objectslayout sparkle tag help sliders window info login card truck megaphone cart envelope speech-bubble kanban calendar table document layers book receipt
Peopleuser users
Chartschart-line chart-bar activity trend-up trend-down
Chromemoon sun palette menu sidebar bell search
Directionchevron-up chevron-down chevron-left chevron-right sort
Markscheck minus close plus kebab eye star currency

Aliases

Product vocabulary pointing at the canonical drawings. Both names are equally valid at the call site; prefer whichever reads correctly in the surface you are building.

AliasResolves toAliasResolves to
aboutinfologisticstruck
analyticschart-barmailenvelope
campaignmegaphonemodalswindow
chatspeech-bubbleorderslayers
componentslayerspaymentscard
contactspeech-bubblepricingtag
contactsuserproductivityactivity
dashboardlayoutsaleschart-line
datatabletablesettingssliders
ecommercecartsigninlogin
faqshelpfeaturessparkle
financecardformsdocument

Extending the set

createEmeraldIconsPlugin takes both halves. icons merges glyphs over the built-in set — an existing key replaces its artwork, a new key extends the vocabulary. aliases adds role → target references, and a bare target is wrapped for you, so { expand: 'chevron-down' } and { expand: '{chevron-down}' } mean the same thing.

main.ts
import { createEmeraldIconsPlugin } from '@paper/emerald'

app.use(createEmeraldIconsPlugin({
  icons: { flame: ['M12 2c3 4 6 6 6 10a6 6 0 0 1-12 0c0-4 3-6 6-10Z'] },
  aliases: { expand: 'chevron-down', trending: 'trend-up' },
}))

A glyph is an array of SVG path d strings, drawn into a 24×24 viewBox with fill="none" and stroke="currentColor". Artwork that assumes a fill will not look right in this set.

Props

PropTypeDefaultDescription
nameEmIconNameRequired. Role to draw, aliases included
labelstringAccessible name. Set only when the icon is the whole message; promotes the icon from decorative to role="img"
size's' | 'm' | 'l' | 'xl''m'Step on the --emerald-icon-* scale
namespacestringRegistry to resolve against. Only needed when a subtree was given its own set

EmIconName accepts any canonical role, any alias, and any string — the last so a set extended at runtime still type-checks. There are no slots.

Accessibility

The component has exactly one accessibility contract, and it is the label prop.

labelRendered attributesReads as
absentaria-hidden="true"Nothing — skipped entirely
setrole="img", aria-label="…"An image with that name

Decorative is the default because it is the correct answer far more often. An icon that sits beside text, inside a labelled button, or as ornament in a heading adds nothing an assistive-technology user needs, and announcing it makes every one of those surfaces noisier.

Two rules follow from the table:

Never label an icon inside an interactive element. The element itself takes the name — ariaLabel on EmButton, the text content of a link. An icon labelled inside a button produces a control whose name is assembled from both, announced twice.

Never rely on an icon alone to convey state without a label. A red glyph meaning “failed” is invisible to a screen reader and to a colorblind reader both. Either label it, or put the state in text nearby and leave the icon decorative.

An empty string is falsy and therefore decorative, not “labelled with nothing” — label="" renders aria-hidden="true".

Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/