Best Practices
Performance optimization and recommended patterns
Preload Critical Icons
Preload icons that appear immediately on page load:
import { useEffect } from 'react'
import { preloadIcons } from 'icondev'
function App() {
useEffect(() => {
// Preload above-the-fold icons
preloadIcons(['logo', 'menu', 'close', 'home'])
}, [])
return <YourApp />
}Preloading eliminates loading flashes and improves perceived performance.
When to preload:
- Navigation icons (menu, close, back)
- Brand icons (logo, company icons)
- Critical UI elements above the fold
- Modal/dialog icons before opening
Optimize Bundle Size
Tree Shaking
Only import what you need for maximum tree-shaking:
// ✅ Good: Specific imports
import { Icon } from 'icondev/react'
import { preloadIcons } from 'icondev'
// ❌ Bad: Wildcard imports
import * as IconDev from 'icondev'Code Splitting
Lazy load icon-heavy components:
import { lazy, Suspense } from 'react'
const IconGallery = lazy(() => import('./components/IconGallery'))
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<IconGallery />
</Suspense>
)
}Caching Strategy
Memory Cache
Icons are automatically cached in memory after first load:
// First render: fetches from CDN (~20ms)
<Icon name="home" />
// Second render: instant (from memory cache)
<Icon name="home" />Memory cache persists until page refresh or clearCache() is called.
Clear Cache When Needed
Only clear cache for memory management (rare):
import { clearCache } from 'icondev'
// Clear cache when unmounting icon-heavy component
useEffect(() => {
return () => {
if (hasLoaded1000PlusIcons) {
clearCache()
}
}
}, [])Loading States
Skeleton Loaders
Show skeleton while icons load:
import { useIcon } from 'icondev/react'
function IconWithSkeleton({ name }) {
const { svg, loading, error } = useIcon(name)
if (loading) {
return (
<div className="animate-pulse">
<div className="w-6 h-6 bg-gray-200 rounded" />
</div>
)
}
if (error) return <span>❌</span>
return <div dangerouslySetInnerHTML={{ __html: svg }} />
}Preload Before Showing
Preload icons before showing them:
function Modal({ isOpen }) {
useEffect(() => {
if (isOpen) {
// Preload modal icons when opening
preloadIcons(['close', 'checkmark', 'warning'])
}
}, [isOpen])
return isOpen && (
<dialog>
<Icon name="close" />
{/* ... */}
</dialog>
)
}SSR Best Practices
Node.js/SSR Environments
Icons automatically read from icondev.json in SSR:
// Works automatically in Next.js, Nuxt, Remix
import { Icon } from 'icondev/react'
export default function Page() {
return <Icon name="home" />
}No special configuration needed for SSR frameworks!
Client-Only Apps
For pure client-side apps, use IconDevProvider:
import { IconDevProvider } from 'icondev/react'
const config = {
apiKey: process.env.NEXT_PUBLIC_ICONDEV_KEY,
projectSlug: 'my-project',
cdnUrl: 'https://cdn.icon.dev'
}
function App() {
return (
<IconDevProvider config={config}>
<YourApp />
</IconDevProvider>
)
}Avoid Hydration Mismatches
Use consistent rendering approach:
'use client'
import { useState, useEffect } from 'react'
import { Icon } from 'icondev/react'
export function ClientIcon({ name, ...props }) {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
// Show skeleton during SSR, real icon after hydration
if (!mounted) {
return <div className="w-6 h-6 bg-gray-200 animate-pulse" />
}
return <Icon name={name} {...props} />
}Error Handling
Graceful Degradation
Always handle icon loading errors:
<Icon
name="home"
onError={(error) => {
console.error('Icon failed to load:', error)
// Could also use fallback UI
}}
/>Fallback Icons
Use fallback UI when icons fail:
import { useIcon } from 'icondev/react'
function IconWithFallback({ name }) {
const { svg, loading, error } = useIcon(name)
if (loading) return <Skeleton />
if (error) return <span>📦</span> // Emoji fallback
return <div dangerouslySetInnerHTML={{ __html: svg }} />
}Performance Monitoring
Measure Icon Load Time
Track icon loading performance:
import { loadIcon } from 'icondev'
async function measureIconLoad(name) {
const start = performance.now()
await loadIcon({ name })
const end = performance.now()
console.log(`Icon "${name}" loaded in ${end - start}ms`)
}Expected Performance
- First load (CDN): 20-50ms
- Cached load (memory): <1ms
- Preloaded icons: <1ms
If icons take >100ms consistently, check your CDN latency or network.
Security
Environment Variables
Store API keys in environment variables:
// .env.local
NEXT_PUBLIC_ICONDEV_KEY=pk_...// usage
const config = {
apiKey: process.env.NEXT_PUBLIC_ICONDEV_KEY,
projectSlug: 'my-project'
}Git & Version Control
Important: icondev.json contains your icon list AND API key.
Recommended approach:
- Commit
icondev.jsonto git - Your app needs the icon list to work - Use environment variables in production - Override API key in production:
// In production, load API key from env
if (process.env.NODE_ENV === 'production') {
// API key comes from env vars in production
process.env.ICONDEV_API_KEY = process.env.ICONDEV_API_KEY || 'pk_...'
}Alternative: .gitignore + CI/CD
If you don't want to commit the API key:
# .gitignore
icondev.jsonThen run npx icondev sync in your CI/CD pipeline before deployment.
Public keys (pk_) are read-only and safe to commit. Secret keys (sk_) should NEVER be committed.
Public vs Private Keys
- Public keys (
pk_): Safe to use in browser, read-only access - Secret keys (
sk_): Never use in browser, write access to project
// ✅ Safe: Public key in browser
const config = {
apiKey: 'pk_...'
}
// ❌ Dangerous: Secret key in browser
const config = {
apiKey: 'sk_...' // Never do this!
}TypeScript
Type Safety
Use TypeScript for better DX:
import type { IconProps } from 'icondev'
const iconConfig: IconProps = {
name: 'home',
size: 24,
color: 'currentColor'
}Future: Auto-generated Icon Names
Coming soon - auto-generated icon name types:
// Future feature
type IconName = 'home' | 'search' | 'user' | 'arrow-right'
<Icon name="home" /> // ✅ Valid
<Icon name="invalid" /> // ❌ Type errorTesting
Mock Icons in Tests
Mock the icondev package in tests:
// __mocks__/icondev.js
export const Icon = ({ name }) => <div data-testid={`icon-${name}`} />
export const loadIcon = async ({ name }) => ({ svg: `<svg>${name}</svg>` })
export const preloadIcons = async () => {}// jest.config.js
module.exports = {
moduleNameMapper: {
'^icondev/react$': '<rootDir>/__mocks__/icondev.js'
}
}Test Icon Rendering
import { render, screen } from '@testing-library/react'
import { Icon } from 'icondev/react'
test('renders icon', async () => {
render(<Icon name="home" />)
const icon = await screen.findByTestId('icon-home')
expect(icon).toBeInTheDocument()
})Accessibility
Add ARIA Labels
Make icons accessible:
<button aria-label="Open menu">
<Icon name="menu" />
</button>Decorative Icons
Hide decorative icons from screen readers:
<div>
<Icon name="star" aria-hidden="true" />
<span>Featured</span>
</div>Semantic Icons
Use semantic HTML with icons:
<button aria-label="Close dialog">
<Icon name="close" />
<span className="sr-only">Close</span>
</button>Next Steps
- API Reference - Full API documentation
- Framework Integration - Next.js, Nuxt, Remix
- Troubleshooting - Common issues