Skip to content

Component Architecture

SVG Icon Systems — astro-icon + lucide-react in Velocity X

Two-stack approach: static sprites at build for cache control, React islands for interactive state. Plus when brand SVGs beat icon libraries.

🎨 ⚡ 🎯
Icon systems are invisible until they break. Most teams slap Font Awesome or Material Icons into their layout and call it done. But at scale — when you care about brand consistency, load time, and design control — your icon strategy compounds every page hit. Velocity X uses a deliberate two-layer approach: **astro-icon for static sprites** (built once, cached forever) and **lucide-react for React islands** (interactive states without SVG duplication). Custom brand SVGs live outside both when the design demands it. **Why icon systems matter.** A single raster icon is 40–100KB. An icon *system* — 200 SVGs as a single sprite file — is 15–30KB, served once, rendering via `` references. That's the performance win. The consistency win is deeper: one source of truth for stroke width, corner radius, visual weight across the entire product. Change your icon library mid-project (say, from Material to Feather), and you're touching hundreds of files. A sprite-based approach isolates the change to one build step. **astro-icon for static content.** astro-icon generates an SVG sprite at build time from a folder of `.svg` files. It auto-inlines the sprite into your HTML (or serves it as a separate file depending on config). In your template, you reference icons by name: ```astro import { Icon } from 'astro-icon'; ``` The Astro compiler resolves these at build time — no runtime lookup, no bundle bloat. You get a single `` in the DOM, and the component renders a lightweight ``. Pages load once, cache hits on every repeat visit. **lucide-react for islands.** Static sprites work for fixed UI. But interactive elements — dropdowns that flip arrow direction on open, buttons that pulse while loading, filters that highlight their icon on active state — need React. lucide-react is a tree-shakeable library of ~460 icons as React components. In Velocity X, we import only what we use (Webpack handles tree-shaking), so a Button with a spinner icon adds ~2KB, not 46KB. ```typescript import { ChevronDown, Loader } from 'lucide-react'; export const FilterDropdown = ({ isOpen, isLoading }) => ( ); ``` The tradeoff: lucide is a library, so you inherit their design language. If your brand icons need custom curves or strokes that deviate from Feather's aesthetic, lucide feels wrong. **Custom brand SVGs.** This is where the third layer lives. Hero animations, marquee elements, custom illustrations that define your brand — these shouldn't come from an icon library. Velocity X keeps custom brand SVGs in `src/assets/illustrations/` and imports them directly in `.astro` or `.tsx` components. You control every point, animate specific paths with GSAP or CSS, and ship them once, fresh from your designer's Figma file. **When to use each layer:** - **astro-icon**: Navigation icons, UI chrome, buttons, badges, filters — anything static and reused 5+ times across the site. - **lucide-react**: Interactive states, dynamic icons (error/success/warning that change), islands that need interactivity. - **Custom SVGs**: Brand hero animations, full-page illustrations, signature visual elements that differentiate you. **The build wins.** Astro generates the sprite in 40–60ms at build time. No runtime cost. No JavaScript parsing for static icons. The sprite inlines into your HTML (or loads as a 2KB external file that compresses to <1KB on the wire), and every icon reference is just 20 bytes of SVG markup. Across a 50-page site with 10 icons per page, you're saving 50KB of markup and eliminating 500 DOM nodes. **The design wins.** You own the source. Fork lucide, rename icons, adjust stroke-widths, adjust corner radii, and rebuild — all in your own `icons/` folder, no library upgrade headaches. Brand consistency is enforced at the sprite level, not via component prop-drilling. **FAQs:** **1. Should I inline the sprite or serve it as a separate file?** Inline for <50 icons; separate file for 100+. Inlining saves a network request (one less file to fetch), but bloats your HTML. A separate sprite file caches independently and works better for multi-page sites where not every page uses every icon. **2. Can I animate astro-icon SVGs?** Yes, with CSS. `` references allow CSS animations on the outer `` element (transform, opacity), but not on individual paths inside the referenced ``. For path-level animation, use custom SVGs or lucide-react components wrapped in a `` (Framer Motion). **3. What if I need icons in different stroke weights?** astro-icon supports a `strokeWidth` prop passed at render time. lucide also accepts `strokeWidth` and `size` props. Custom SVGs: design all weights in Figma, export separately. Pick one approach and stick with it. **4. Does tree-shaking really matter for lucide?** On a 2KB page (typical marketing site), importing 5 lucide icons adds ~3–5KB. On a 200KB SPA (admin dashboard), it's invisible. If bundle size is your bottleneck, astro-icon + custom SVGs beat lucide. If interactivity is your constraint, lucide's size is worth the flexibility. **5. How do I handle icon colors / theming?** astro-icon: use Tailwind classes (`text-blue-600`, `dark:text-slate-200`). lucide: pass `color` or `className` to the component. Custom SVGs: embed `fill="currentColor"` to inherit from parent text color, or use CSS custom properties in your stylesheet. **6. What if my design team changes the icon set mid-project?** With astro-icon: drop new SVGs into `icons/`, rebuild, done. References stay the same. With lucide: if you're upgrading library versions, run `npx lucide-icon@latest sync` (or maintain a wrapper component that maps your old icon names to lucide names). With custom SVGs: re-export from Figma. The sprite approach isolates the pain to one folder; scattered icon props across components makes it exponentially worse. **The verdict:** Icon systems are a multiplier on design quality and load time. Velocity X's two-layer approach — sprites for static, lucide for reactive, custom SVGs for hero moments — is the pattern that scales. You get cache hits on icon assets, design control without library opinion debt, and a clear mental model for new team members. Start with astro-icon for your core UI. Reach for lucide when interactivity demands it. Roll custom SVGs for moments that define your brand. Own the whole stack, and your design system stays tight.
Let us make some quick suggestions?
Please provide your full name.
Please provide your phone number.
Please provide a valid phone number.
Please provide your email address.
Please provide a valid email address.
Please provide your brand name or website.
Please provide your brand name or website.