What if you didn't need a dedicated Ember addon to use icons. Since Ember runs on standard build tooling (Vite or webpack), you can tap into unplugin-icons directly. It gives you on-demand access to 200,000+ icons from 150+ icon sets, all loaded as components. Only the icons you import end up in your bundle. It works with both Vite and webpack, so it covers both modern and classic Ember setups.
The Ember compiler landed in unplugin-icons v0.22. Here's how to set it up.
1. Install the dependencies
You need two things: the plugin itself and at least one icon set. Lucide is a solid default (clean, consistent, MIT-licensed), but you can pick whatever you want from Iconify's collection.
pnpm add -D unplugin-icons @iconify-json/lucide
Want multiple sets? Install them all:
pnpm add -D unplugin-icons @iconify-json/lucide @iconify-json/simple-icons
2. Configure your build tool
Vite (recommended)
If your app uses @embroider/vite, open vite.config.mjs and add the plugin:
import Icons from 'unplugin-icons/vite';
export default defineConfig({
plugins: [
ember(),
// ... your other plugins
Icons({ compiler: 'ember' }),
],
});
Webpack
If your app uses Embroider with webpack (or classic ember-auto-import), add the plugin to your ember-cli-build.js:
const Icons = require('unplugin-icons/webpack');
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
// ...
});
return require('@embroider/compat').compatBuild(app, require('@embroider/webpack').Webpack, {
packagerOptions: {
webpackConfig: {
plugins: [Icons({ compiler: 'ember' })],
},
},
});
};
Vite is the recommended path forward for Ember apps. If you're starting a new project, go with Vite.
3. Use icons in your components
Import any icon using the ~icons/{collection}/{icon-name} convention and drop it into your template like any other component:
import Search from '~icons/lucide/search';
import ChevronDown from '~icons/lucide/chevron-down';
import GithubIcon from '~icons/simple-icons/github';
<template>
<button type="button">
<Search />
Search...
<ChevronDown />
</button>
<a href="https://github.com">
<GithubIcon />
</a>
</template>
Icons render as inline SVGs. They inherit currentColor by default, so they follow your text color. Size them with font-size, width/height, or a utility class.
import Check from '~icons/lucide/check';
<template>
{{! Sized via Tailwind }}
<Check class="size-4" />
{{! Sized via inline style }}
<Check style="width: 2em; height: 2em;" />
</template>
Finding icon names
Browse icons at icones.js.org or icon-sets.iconify.design. The collection name and icon name map directly to the import path:
- Collection:
lucide, icon:arrow-right→~icons/lucide/arrow-right - Collection:
simple-icons, icon:emberdotjs→~icons/simple-icons/emberdotjs - Collection:
mdi, icon:account-box→~icons/mdi/account-box
If you use VS Code, install the Iconify IntelliSense extension. It gives you inline previews and autocomplete for icon names.
Custom SVG icons
You can load your own SVGs too. Use FileSystemIconLoader to point at a directory:
import { FileSystemIconLoader } from 'unplugin-icons/loaders';
Icons({
compiler: 'ember',
customCollections: {
custom: FileSystemIconLoader('./app/assets/icons'),
},
});
Then import them the same way:
import Logo from '~icons/custom/logo';
<template>
<Logo />
</template>
Make sure your SVGs use currentColor for fills/strokes if you want them to adapt to text color.
TypeScript
Add unplugin-icons/types/ember to the types array in your tsconfig.json:
{
"compilerOptions": {
"types": [
"unplugin-icons/types/ember"
]
}
}
This gives you proper module declarations for ~icons/* imports so TypeScript stops complaining.
That's all
Same dependencies, same imports, same compiler: 'ember'. Whether you're on Vite or webpack, the DX is identical. No icon fonts, no sprite sheets, no build scripts. The icons are tree-shaken automatically: if you don't import it, it doesn't exist in your bundle.
The full list of supported icon sets lives at icones.js.org.
Top comments (0)