Framework Integration
Use icondev with Next.js, Nuxt, Remix, and more
Overview
The icondev package integrates seamlessly with modern JavaScript frameworks. This guide covers framework-specific configurations and best practices.
Next.js
App Router (Next.js 13+)
Icons work out of the box in Server Components during SSR (reads from icondev.json). For client-side usage, use Client Components:
'use client'
import { Icon } from 'icondev/react'
export function IconButton({ icon, label, onClick }) {
return (
<button onClick={onClick} className="flex items-center gap-2">
<Icon name={icon} size={20} />
<span>{label}</span>
</button>
)
}import { IconButton } from './components/icon-button'
export default function Page() {
return (
<div>
<h1>Welcome</h1>
<IconButton icon="arrow-right" label="Continue" onClick={() => {}} />
</div>
)
}In Node.js/SSR environments, icons load from icondev.json. In browser, you need IconDevProvider (see below).
Browser-Only Usage (IconDevProvider)
If you're using icons only on the client side without SSR, wrap your app with IconDevProvider:
'use client'
import { IconDevProvider } from 'icondev/react'
const config = {
apiKey: 'pk_...',
projectSlug: 'my-project',
cdnUrl: 'https://cdn.icon.dev'
}
export default function RootLayout({ children }) {
return (
<html>
<body>
<IconDevProvider config={config}>
{children}
</IconDevProvider>
</body>
</html>
)
}Preloading Critical Icons
'use client'
import { useEffect } from 'react'
import { preloadIcons } from 'icondev'
export default function RootLayout({ children }) {
useEffect(() => {
// Preload icons used in layout
preloadIcons(['logo', 'menu', 'close'])
}, [])
return (
<html>
<body>{children}</body>
</html>
)
}Nuxt 3
Setup
Icons work automatically in Nuxt 3! The package reads from icondev.json during SSR.
<script setup>
import { Icon } from 'icondev/vue'
</script>
<template>
<div>
<Icon name="home" :size="24" />
</div>
</template>Preload Icons Plugin
Create a plugin to preload critical icons:
import { preloadIcons } from 'icondev'
export default defineNuxtPlugin(() => {
// Preload critical icons
preloadIcons(['logo', 'menu', 'close'])
})The .client.ts suffix ensures the plugin only runs on the client side.
Using Icons in Components
<script setup lang="ts">
import { Icon } from 'icondev/vue'
defineProps<{
icon: string
label: string
}>()
</script>
<template>
<button class="flex items-center gap-2">
<Icon :name="icon" :size="20" />
<span>{{ label }}</span>
</button>
</template><script setup lang="ts">
import IconButton from '~/components/IconButton.vue'
</script>
<template>
<div>
<h1>Welcome</h1>
<IconButton icon="arrow-right" label="Continue" />
</div>
</template>Auto-imports
Add to nuxt.config.ts for auto-imports:
export default defineNuxtConfig({
imports: {
imports: [
{
from: 'icondev/vue',
name: 'Icon'
},
{
from: 'icondev/vue',
name: 'useIcon'
}
]
}
})Now use without imports:
<template>
<Icon name="home" :size="24" />
</template>Remix
Setup
Icons work automatically in Remix! The package reads from icondev.json during SSR.
import { Icon } from 'icondev/react'
export default function Index() {
return (
<div>
<h1>Welcome to Remix</h1>
<Icon name="home" size={24} />
</div>
)
}Root Layout Preloading
import { useEffect } from 'react'
import { preloadIcons } from 'icondev'
import { Links, Meta, Outlet, Scripts } from '@remix-run/react'
export default function App() {
useEffect(() => {
preloadIcons(['logo', 'menu'])
}, [])
return (
<html>
<head>
<Meta />
<Links />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
)
}Vite + React
Standard Setup
import { Icon } from 'icondev/react'
function App() {
return (
<div>
<Icon name="home" size={24} />
</div>
)
}
export default AppBrowser-Only (IconDevProvider)
For pure client-side apps without SSR:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { IconDevProvider } from 'icondev/react'
import App from './App'
const config = {
apiKey: 'pk_...',
projectSlug: 'my-project',
cdnUrl: 'https://cdn.icon.dev'
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<IconDevProvider config={config}>
<App />
</IconDevProvider>
</StrictMode>
)Preload on Mount
import { StrictMode, useEffect } from 'react'
import { createRoot } from 'react-dom/client'
import { preloadIcons } from 'icondev'
import App from './App'
function Root() {
useEffect(() => {
preloadIcons(['logo', 'menu', 'close'])
}, [])
return <App />
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Root />
</StrictMode>
)Astro
React Integration
'use client'
import { Icon } from 'icondev/react'
export function IconButton({ icon, label }) {
return (
<button>
<Icon name={icon} size={20} />
<span>{label}</span>
</button>
)
}---
import { IconButton } from '../components/IconButton'
---
<html>
<body>
<h1>Welcome</h1>
<IconButton client:load icon="arrow-right" label="Continue" />
</body>
</html>Use client:load directive to ensure icons render on the client.
Svelte / SvelteKit
Using Core API
While we don't have an official Svelte adapter yet, you can use the core API:
<script>
import { onMount } from 'svelte'
import { loadIcon } from 'icondev'
export let name
export let size = 24
let svg = ''
let loading = true
onMount(async () => {
try {
const result = await loadIcon({ name })
svg = result.svg
} catch (error) {
console.error('Failed to load icon:', error)
} finally {
loading = false
}
})
</script>
{#if loading}
<div class="skeleton" style="width: {size}px; height: {size}px;" />
{:else}
{@html svg}
{/if}<script>
import Icon from '$lib/Icon.svelte'
</script>
<Icon name="home" size={24} />Angular
Using Core API
Use the core API with Angular:
import { Component, Input, OnInit } from '@angular/core'
import { DomSanitizer, SafeHtml } from '@angular/platform-browser'
import { loadIcon } from 'icondev'
@Component({
selector: 'app-icon',
template: `
<div *ngIf="loading">Loading...</div>
<div *ngIf="!loading && svg" [innerHTML]="svg"></div>
`,
standalone: true
})
export class IconComponent implements OnInit {
@Input() name!: string
@Input() size: number = 24
svg: SafeHtml | null = null
loading = true
constructor(private sanitizer: DomSanitizer) {}
async ngOnInit() {
try {
const result = await loadIcon({ name: this.name })
this.svg = this.sanitizer.bypassSecurityTrustHtml(result.svg)
} catch (error) {
console.error('Failed to load icon:', error)
} finally {
this.loading = false
}
}
}import { Component } from '@angular/core'
import { IconComponent } from './icon.component'
@Component({
selector: 'app-root',
template: `
<div>
<h1>Welcome</h1>
<app-icon name="home" [size]="24"></app-icon>
</div>
`,
standalone: true,
imports: [IconComponent]
})
export class AppComponent {}Server-Side Rendering (SSR)
How It Works
Icons work seamlessly with SSR:
- In Node.js/SSR: Package reads icon metadata from
icondev.json - In Browser (with SSR): Icons load from CDN using URLs from
icondev.json - In Browser (without SSR): Requires
IconDevProviderto configure CDN URLs
Best Practices
✅ DO: Use icons in SSR frameworks (Next.js, Nuxt, Remix)
// Works automatically in SSR
import { Icon } from 'icondev/react'
export default function Page() {
return <Icon name="home" />
}✅ DO: Use IconDevProvider for client-only apps
import { IconDevProvider } from 'icondev/react'
function App() {
return (
<IconDevProvider config={{ apiKey: 'pk_...', projectSlug: 'my-project' }}>
<YourApp />
</IconDevProvider>
)
}Loading States
Add skeleton loaders for better UX:
'use client'
import { Icon } from 'icondev/react'
import { useState, useEffect } from 'react'
export function IconWithSkeleton({ name }) {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
if (!mounted) {
return <div className="skeleton w-6 h-6" />
}
return <Icon name={name} size={24} />
}Build Optimization
Tree Shaking
The package supports tree shaking. Only import what you need:
// ✅ Good: Only imports Icon component
import { Icon } from 'icondev/react'
// ❌ Avoid: Imports everything
import * as IconDev from 'icondev'Code Splitting
Lazy load icons for better performance:
import { lazy, Suspense } from 'react'
const IconGallery = lazy(() => import('./components/IconGallery'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<IconGallery />
</Suspense>
)
}Next Steps
- API Reference - Full API documentation
- Best Practices - Performance optimization
- Troubleshooting - Common issues