Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do you dynamically load an Icon? #34

Open
ElixirMike opened this issue Jan 27, 2020 · 14 comments
Open

How do you dynamically load an Icon? #34

ElixirMike opened this issue Jan 27, 2020 · 14 comments
Labels
question Further information is requested

Comments

@ElixirMike
Copy link

So, in my app, I want to provide an Icon picker...let the user choose their icon. Once saved, I would use it on other pages in the app.

How do I dynamically import this specific icon on those pages?

@Templarian Templarian added the question Further information is requested label Jan 27, 2020
@Templarian
Copy link
Owner

Yeah, we need to document this process and how it should work. It unfortunately requires a server-side component if one wants to use all 5k icons or it can become a performance issue for pages with lots of icons.

Do you want to provide all ~5k icons in the icon picker or a subset? We/community need to make some demos of both examples.

@ElixirMike
Copy link
Author

I was just looking to create a subset...we've picked out about 800 icons to use. For now, what I did was create a custom object with array of icons, including a tag, name of icon and path.

Like this where I insert the 800 icons.
var myicons = [{tag:'phones', name='mdiphone', path: "....full svg path", {}, {}, etc ]

It's lazy loaded on icon picker page..but when a user selects the icon, I simply store the svg path in db/cache.

Then, in the rest of my application, I can simply use the svgpath and display, so it's very fast.

It would be super helpeful to have someway of picking icons you want and then generating this type of object which can be used to display them on icon selection page.

@AmirHosseinKarimi
Copy link

I am trying to use dynamic import from this article
But in this article only can import whole file, refer to @mdi/js/mdi.js all icons are stored to this single file.
Is there anyway to dynamically import specific icon from this file?

@Templarian
Copy link
Owner

Templarian commented May 3, 2020

@AmirHosseinKarimi You could use the @mdi/svg package and fetch the individual SVG's by their icon name in a wrapper <LazyIcon name={iconName}>. Since <Icon> takes any path data, you could regex for the path data out of the SVG request and put it into <Icon>.

const data = body.match(/d="([^"]+)"/)[1];

Obviously write a light cache layer into your component so you don't make unnecessary requests for the path data.

@Templarian
Copy link
Owner

The @mdi/js package is only really aimed at compile time for tree-shaking. The @mdi/svg also contains the meta.json if you need to generate a list of icons (recommend to trim down this file as it could be heavy for use on the web).

@AmirHosseinKarimi
Copy link

Sorry I did not understand

@Templarian
Copy link
Owner

@AmirHosseinKarimi https://github.com/Templarian/MaterialDesign-SVG contains individual SVG files you can deploy. These could be requested by their name in a wrapping component. You only need the SVG path data aka <path d="this content"/> so that regex will get that (made a typo and edited it above with the correct regex).

@AmirHosseinKarimi
Copy link

AmirHosseinKarimi commented May 4, 2020

So I have created a package (materialdesign-js) to dynamically load an icon.
The description is clear about how this package working.
This package is a dependency of another package, which I will create for React that automatically load the icon.

Currently if you need a dynamically load icon, you can create the following component:

import React from "react";
import { Icon as MDIcon } from "@mdi/react";

class Icon extends React.Component {
  render() {
    let icon = require(`materialdesign-js/icons/${this.props.icon}`).default;
    if (!icon) {
      throw Error(`Could not find materialdesign-js/icons/${icon}`);
    }
    return <MDIcon path={icon} />;
  }
}

export default Icon;

and use like this:

import Icon from "./Icon";
...
<Icon icon="mdiAccount" />

If this package is helped you please give a star: GitHub stars

Also if you like to contribute, just look at the MaterialDesign-JS repository.

@Templarian
Copy link
Owner

We recommend using a peer dependency for @mdi/js. Referencing a specific version usually doesn't work out in the long run. Learn more:

Yours will be more lightweight than the @mdi/svg approach, but similar idea loading individual glyphs.

We will work to open source the IndexedDB solution as it will be more ideal performance wise. Also makes it easier in cases where one may need to search all icons in a list.

@AmirHosseinKarimi
Copy link

Good, I create this package because I need dynamic load icons in my current project.
So always there is an better idea. I am waiting for IndexedDB solution to try it.

@AmirHosseinKarimi
Copy link

Finally I have find a way which can use dynamic load MDI and also do not decrease the performance of app in development and runtime.
Just look at this gist: https://gist.github.com/AmirHosseinKarimi/801931665a0845ccc25ed486431abf72

@craigrileyuk
Copy link

https://gist.github.com/AmirHosseinKarimi/801931665a0845ccc25ed486431abf72

You're requiring the entire @mdi/js library here, so any development or production build of the app would have to include the whole package (around 2.3MB).

@AmirHosseinKarimi
Copy link

@craigrileyuk Yea you're right. It's a little bit old. But I think the dynamic load example in the MaterialDesign-JS repository readme is correct and working.

@ironAngel2000
Copy link

ironAngel2000 commented Mar 15, 2024

maybe a little late but this works for me fine:

import React, { useEffect, useState } from "react";
import { Icon as MDIcon } from "@mdi/react";

type props = {
    icon: string,
    size: number,
}

const LazyIcon = (props: props) => {

    const [iconPath, setIconPath] = useState<string>('');

    useEffect(() => {
        import('@mdi/js').then((icons:any) => {
            if (icons[props.icon]) {
                const icon = icons[props.icon];
                setIconPath(icon.toString());
            }
        });
    }, [props.icon]);

    return (<>{iconPath !== '' ?
        <MDIcon path={iconPath} size={props.size}></MDIcon>
        : null
    }</>);

}

export { LazyIcon };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

5 participants