Comments (7)
You can render an svg element pointing to tabler-sprite-nostroke.svg#ICON_NAME
whether its in node_modules or wherever
type Props = {
icon: string;
color?: string;
size?: number;
};
export function Icon({ icon, color = "inherit", size = 18 }: Props) {
return (
<svg
style={{
stroke: "currentColor",
strokeWidth: 1.75,
strokeLinecap: "round",
strokeLinejoin: "round",
fill: "none",
color: color,
width: `${size}px`,
height: `${size}px`
}}
>
<use href={"path/to/node_modules/@tabler/icons/tabler-sprite-nostroke.svg#tabler-" + icon} />
</svg>
);
}
This is how I was doing it at first but I had issues with Electron loading in the entire sprite sheet for each icon in production mode (worked fine in dev mode), so I also have the Tabler Icons Webfont package and use this instead:
import "@tabler/icons-webfont/tabler-icons.min.css";
type Props = {
icon: string;
color?: string;
size?: number;
};
export function Icon({ icon, color = "inherit", size = 18 }: Props) {
return (
<i
className={`ti ti-${icon}`}
style={{
fontSize: `${size}px`,
color: color
}}
/>
);
}
from tabler-icons.
Have you tried rendering a list of all these icons?
from tabler-icons.
I support the addition of this capability 👍
I'm currently working on a component that requires the ability to accept an icon name as a prop and dynamically render it inside. Having this feature would greatly enhance the flexibility of the component.
Additionally, it would be beneficial to have an enum or type that provides a comprehensive list of all available icons. This way, developers can easily reference and choose from a standardized set of icons when using the component.
from tabler-icons.
Here is my attempt:
(Optional: get a list of all available icons)
Then Display the icon by its name.
import { icons } from "@tabler/icons-react";
import { useState } from "react";
import dynamic from "next/dynamic";
export default function Home() {
const [icon, setIcon] = useState<string>("IconAbc");
return (
<main className={`flex min-h-screen flex-col items-center p-24 gap-4`}>
<DynamicIcon icon={icon} />
{icon}
<div className="grid grid-cols-12 h-96 overflow-scroll">
{Object.entries(icons).map(([key, Value]) => (
<button
className="p-1 hover:bg-red-500"
key={key}
onClick={() => setIcon(key)}
title={key}
>
<Value />
</button>
))}
</div>
</main>
);
}
const DynamicIcon = ({ icon }: { icon?: string }) => {
if (!icon) return null;
const Icon = dynamic(
() => import(`@tabler/icons-react/dist/esm/icons/${icon}.mjs`),
{
loading: () => <p>Loading...</p>,
}
);
return <Icon />;
};
from tabler-icons.
Update 1
The problem with my previously mentioned solution is that Webpack generates an entry map with all kinds of icons when it is created, which is added when the page is first loaded and is more than 50kb.
Looks like this:
"./IconGalaxy.mjs":[183111,183111],"./IconGardenCart.mjs":[643864,643864],"./IconGardenCartOff.mjs":[49835,49835],"./IconGasStation.mjs":[913266,913266],"./IconGasStationOff.mjs":[695510,695510],"./IconGauge.mjs":[890866,890866],"./IconGaugeFilled.mjs":[161194,161194],"./IconGaugeOff.mjs":[622710,622710],"./IconGavel.mjs":[103125,103125],"./IconGenderAgender.mjs":[467566,467566],"./IconGenderAndrogyne.mjs":[951129,951129],"./IconGenderBigender.mjs":[489974,489974],"./IconGenderDemiboy.mjs":[971931,971931],"./IconGenderDemigirl.mjs":[158563,158563],"./IconGenderEpicene.mjs":[750659,750659],"./IconGenderFemale.mjs":[883878,883878],"./IconGenderFemme.mjs":[527029,527029],"./IconGenderGenderfluid.mjs":[226052,226052],"./IconGenderGenderless.mjs":[640555,640555],"./IconGenderGenderqueer.mjs":[618596,618596],"
The icons are bundled in individual chunks, which I think is correct behavior.
Does anyone know how to optimize this to avoid the 50kb
first-load, or does anyone know a better implementation?
Update 2
I found out that if you create an object that contains all icons -> dynamic import
and then dynamically import it, the 50kb
chunk is split on first load.
dynamicImports.ts
import { iconsList } from "@tabler/icons-react";
import set from "lodash/set";
import type { FC } from "react";
import { iconToPascalCase } from "./utils";
export const dynamicIconImports: Record<
(typeof iconsList)["default"][number],
() => Promise<FC>
> = iconsList.default.reduce((prev, icon) => {
const pascalIconName = iconToPascalCase(icon);
set(
prev,
pascalIconName,
() => import(`@tabler/icons-react/dist/esm/icons/Icon${pascalIconName}.mjs`)
);
return prev;
}, {});
Then import into the dynamicIcon component.
dynamicIcon.tsx
import { IconFileUnknown, IconLoader } from "@tabler/icons-react";
import dynamic from "next/dynamic";
const LoadingIcon = () => (
<IconLoader className="animate-pulse" aria-label="Icon wird geladen..." />
);
export const DynamicIcon = (icon: string)=> {
const Icon = dynamic(
async () => {
const importMap = (await import(`./dynamicImports`)).dynamicIconImports;
return importMap[icon]?.().catch(() => ({
default: () => <IconFileUnknown className="opacity-50" />,
}));
},
{
loading: LoadingIcon,
}
);
return Icon;
};
Now it's a separate chunk. Not ideal, but still better than having 50kb
in the first load.
Suggestion
If this object containing the dynamic imports were provided by @tabler/icons-react
, webpack could statically analyze it and store potential network requests and data.
This also eliminates the need to convert the iconsList to pascalCase on the end user's side.
const dynamicIconImports = {
"a-b-2": () => import(`@tabler/icons-react/dist/esm/icons/IconAB2.mjs`),
"a-b-off": () => import(`@tabler/icons-react/dist/esm/icons/IconABOff.mjs`),
...
}
I will try to make a PR later.
from tabler-icons.
Update 1
The problem with my previously mentioned solution is that Webpack generates an entry map with all kinds of icons when it is created, which is added when the page is first loaded and is more than 50kb.
Looks like this:
"./IconGalaxy.mjs":[183111,183111],"./IconGardenCart.mjs":[643864,643864],"./IconGardenCartOff.mjs":[49835,49835],"./IconGasStation.mjs":[913266,913266],"./IconGasStationOff.mjs":[695510,695510],"./IconGauge.mjs":[890866,890866],"./IconGaugeFilled.mjs":[161194,161194],"./IconGaugeOff.mjs":[622710,622710],"./IconGavel.mjs":[103125,103125],"./IconGenderAgender.mjs":[467566,467566],"./IconGenderAndrogyne.mjs":[951129,951129],"./IconGenderBigender.mjs":[489974,489974],"./IconGenderDemiboy.mjs":[971931,971931],"./IconGenderDemigirl.mjs":[158563,158563],"./IconGenderEpicene.mjs":[750659,750659],"./IconGenderFemale.mjs":[883878,883878],"./IconGenderFemme.mjs":[527029,527029],"./IconGenderGenderfluid.mjs":[226052,226052],"./IconGenderGenderless.mjs":[640555,640555],"./IconGenderGenderqueer.mjs":[618596,618596],"The icons are bundled in individual chunks, which I think is correct behavior.
Does anyone know how to optimize this to avoid the
50kb
first-load, or does anyone know a better implementation?Update 2
I found out that if you create an object that contains all
icons -> dynamic import
and then dynamically import it, the50kb
chunk is split on first load.dynamicImports.ts
import { iconsList } from "@tabler/icons-react"; import set from "lodash/set"; import type { FC } from "react"; import { iconToPascalCase } from "./utils"; export const dynamicIconImports: Record< (typeof iconsList)["default"][number], () => Promise<FC> > = iconsList.default.reduce((prev, icon) => { const pascalIconName = iconToPascalCase(icon); set( prev, pascalIconName, () => import(`@tabler/icons-react/dist/esm/icons/Icon${pascalIconName}.mjs`) ); return prev; }, {});Then import into the dynamicIcon component.
dynamicIcon.tsx
import { IconFileUnknown, IconLoader } from "@tabler/icons-react"; import dynamic from "next/dynamic"; const LoadingIcon = () => ( <IconLoader className="animate-pulse" aria-label="Icon wird geladen..." /> ); export const DynamicIcon = (icon: string)=> { const Icon = dynamic( async () => { const importMap = (await import(`./dynamicImports`)).dynamicIconImports; return importMap[icon]?.().catch(() => ({ default: () => <IconFileUnknown className="opacity-50" />, })); }, { loading: LoadingIcon, } ); return Icon; };Now it's a separate chunk. Not ideal, but still better than having
50kb
in the first load.Suggestion
If this object containing the dynamic imports were provided by
@tabler/icons-react
, webpack could statically analyze it and store potential network requests and data.This also eliminates the need to convert the iconsList to pascalCase on the end user's side.
const dynamicIconImports = { "a-b-2": () => import(`@tabler/icons-react/dist/esm/icons/IconAB2.mjs`), "a-b-off": () => import(`@tabler/icons-react/dist/esm/icons/IconABOff.mjs`), ... }I will try to make a PR later.
I tested your solution from 3 weeks ago, but I felt the application significantly weighed down the page loading. So, I refactored it in a simpler and more direct way to achieve what I wanted, and I noticed an improvement in performance. I didn't have time to deeply analyze the reason for the improvement, but that's what I was able to do in the short time I had:
import { getIconInPascalCase } from '@/utils/icons/getIconInPascalCase'
import * as icons from '@tabler/icons-react'
import { memo } from 'react'
type Props = icons.TablerIconsProps & {
icon: string
}
type IconProps = (props: Props) => JSX.Element | null
export const DynamicIcon = memo<IconProps>(function DynamicIcon({
icon,
name,
...props
}) {
if (!icon) return null
const iconName = getIconInPascalCase(icon)
const Icon = icons[iconName] as unknown as IconProps
return <Icon icon={iconName} name={name} {...props} />
})
from tabler-icons.
@tsnery This is because you are importing the entire namespace. Your app may feel faster, but you download every icon in the first-load 400kb gzipped
. You can test that with bundle-analyzer.
from tabler-icons.
Related Issues (20)
- [Icon request] circle-dashed-check HOT 1
- Website outlined/filled hiding icons HOT 1
- vehicle icons filled
- [Icon Request] Text Bubble HOT 1
- `IconProps` type is incompatible with icons components HOT 3
- Brave Browser Icons HOT 1
- [Icon request] brand-codeberg
- [Icon request] brand-bun
- Suggestion - submit your icons here to penpot be used
- No PentagonMinus HOT 1
- [Icon Request] Kubernetes
- [Icon Request] Passport Control
- [Icon Update] Storj
- Dynamic usage of icons don`t accept any props in 3.2.x HOT 1
- Stylesheet webfont path is wrong
- Allow css/webfont to work out of the box in more settings
- [Icon Request] xth-root
- [Icon Request] Coins Plus
- [Icon Update] Deezer
- [Icon Request] Search Patterns
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tabler-icons.