I built Material Symbols SVG, an icon library that lets you use Google's Material Symbols as SVG components across frameworks.
It currently supports:
- React:
@material-symbols-svg/react - Vue:
@material-symbols-svg/vue - Svelte:
@material-symbols-svg/svelte - Astro:
@material-symbols-svg/astro - React Native:
@material-symbols-svg/react-native
Links:
- Docs: https://www.material-symbols-svg.com/usage
- Icon catalog: https://www.material-symbols-svg.com/
- GitHub: https://github.com/k-s-h-r/material-symbols-svg
Why I built it
There is already an official-ish way to consume Material Symbols as SVG files, for example @material-symbols/svg-400.
The problem I kept running into was this: if I wanted to use those icons in React as components, I usually had to rely on another layer such as @svgr/webpack, or build some custom conversion flow around raw SVG files.
That works, but it is not what I wanted for day-to-day UI work.
What I wanted instead was:
- no extra SVG conversion step
- no framework-specific webpack setup just to render icons
- direct imports that already behave like components
- a consistent mental model across frameworks
So I built a library that ships Material Symbols as framework components from the start.
What the library provides
At the moment, the library includes:
- 3,800+ icons
- 3 style variants:
outlined,rounded,sharp - 7 weight variants:
100through700 - filled variants
- framework packages with similar import patterns
Because these are distributed as SVG components rather than icon fonts, they fit nicely into regular component-based UI code. You can control them with things like size, color, className or class, style, and standard SVG attributes.
Basic usage
Here is a React example:
import { Home, Search } from "@material-symbols-svg/react";
import { Home as HomeRounded } from "@material-symbols-svg/react/rounded";
import { Home as HomeW500 } from "@material-symbols-svg/react/w500";
export function Example() {
return (
<div style={{ display: "flex", gap: 12, alignItems: "center" }}>
<Home />
<Search size={20} color="#64748b" />
<HomeRounded size={28} />
<HomeW500 size={28} color="#0ea5e9" />
</div>
);
}
The default import is outlined / w400.
import { Home } from "@material-symbols-svg/react"; // outlined / w400
import { Home as HomeW600 } from "@material-symbols-svg/react/w600";
import { Home as HomeRounded } from "@material-symbols-svg/react/rounded";
import { Home as HomeSharpW700 } from "@material-symbols-svg/react/sharp/w700";
The same import-path idea is used across the other framework packages too.
Why style and weight are split by import path
One of the main design choices in this library is that style and weight are selected by import path, not by runtime props.
That is not just an API preference. It comes from how Material Symbols SVG data is structured.
Material Symbols are not simple line icons where you can change stroke width at runtime and get a new weight. Each style and weight combination has its own filled path data.
That means:
-
outlined,rounded, andsharpare different SVG paths -
w100throughw700are also different SVG paths
So if I designed the API like this:
<Home styleVariant="rounded" weight={600} />
then a single icon component would tend to carry up to 3 x 7 = 21 path variants inside one module.
That is convenient at the call site, but it is not great for module boundaries and tree-shaking. Unused variants are much more likely to sit inside the same import graph.
By splitting variants into import paths like:
@material-symbols-svg/react/w600@material-symbols-svg/react/rounded/w400@material-symbols-svg/react/sharp/w700
the bundler has a much clearer module shape to work with.
Deep imports for individual icons
The library also supports deep imports through icons/*.
import { HomeW400, HomeFillW600 } from "@material-symbols-svg/react/icons/home";
import { SettingsW400 } from "@material-symbols-svg/react/rounded/icons/settings";
This helps for a few reasons:
- the exact icon is explicit in the import path
- filled variants are easy to target
- in some setups, dev server startup and HMR behave better than broad root imports
This part became especially important when I tested framework-specific developer experience.
For example, in Astro 5 I hit cases where root imports timed out during dev startup, while icons/* deep imports worked reliably. So for some frameworks, deep imports are not just a nice extra API, but a practical escape hatch for development performance.
Standard SVG props and accessibility
The components accept standard SVG-oriented props and attributes such as:
sizecolor-
className/class stylearia-*data-*
Example:
import { Home } from "@material-symbols-svg/react";
export function Example() {
return (
<Home
size={32}
color="#ff6b6b"
className="drop-shadow-lg"
aria-label="Home"
data-testid="home-icon"
/>
);
}
You can also pass a <title> as a child:
<Home>
<title>Home</title>
</Home>
which ends up inside the rendered SVG:
<svg ...>
<path ...></path>
<title>Home</title>
</svg>
That keeps accessibility metadata close to the icon component itself.
Tailwind CSS works well too, because you can style the icon just like any other component:
import { Search } from "@material-symbols-svg/react";
export function SearchButton() {
return (
<button className="inline-flex items-center gap-2 rounded-md border px-3 py-2 text-sm hover:bg-slate-50">
<Search className="size-5 text-slate-600" />
Search
</button>
);
}
Next.js optimization
For the React package, Next.js users can also improve dev-time behavior with optimizePackageImports:
const nextConfig = {
experimental: {
optimizePackageImports: [
"@material-symbols-svg/react",
"@material-symbols-svg/react/rounded",
"@material-symbols-svg/react/sharp",
],
},
};
export default nextConfig;
Official docs:
https://nextjs.org/docs/app/api-reference/config/next-config-js/optimizePackageImports
Next.js already optimizes some libraries by default, such as lucide-react and react-icons/*, but @material-symbols-svg/react is not included in that default list, so it needs to be configured explicitly.
I also built an icon catalog site
Along with the usage docs, I built a site where you can browse the icons and inspect variants:
https://www.material-symbols-svg.com/
It lets you:
- search by icon name
- compare style, fill, weight, size, and color
- check framework-specific import snippets
- copy the SVG directly
When you are dealing with thousands of icons, that kind of browsing tool matters more than I expected. It is useful not just for common UI icons, but also for the weird niche ones.
One thing I really like about Material Symbols is that the set is huge enough that you keep finding icons you did not expect to exist. There are very specific icons in there, including things like vacuum-related icons. My personal favorite is still owl.
Closing
I built this because I wanted Material Symbols to feel like first-class components instead of raw SVG assets that need another toolchain step.
If that sounds useful, take a look here:
- Docs: https://www.material-symbols-svg.com/usage
- Icon catalog: https://www.material-symbols-svg.com/
- GitHub: https://github.com/k-s-h-r/material-symbols-svg
If you try it and have feedback, I would love to hear it.
Top comments (0)