lucide-icons / lucide

Beautiful & consistent icon toolkit made by the community. Open-source project and a fork of Feather Icons.
https://lucide.dev
ISC License
9.49k stars 412 forks source link

Dynamic Import makes NextJS development server very slow #1576

Open morrisseybr opened 9 months ago

morrisseybr commented 9 months ago

Package

Version

0.279.0

Browser

Operating system

Description

I'm attempting to utilize the dynamicIconImports object to create a unified component for rendering icons based on an icon name prop in NextJS 13 with the app router. I followed the example provided in the documentation, but it significantly slows down my development server. The time it takes for the initial compilation has increased from ~8 seconds to ~22 seconds, and the time for hot reloading even minor changes (such as a simple text modification) has gone from ~800ms to ~12 seconds.

image

I've tried changing the component from server-side to client-side rendering. I've also attempted to add the library to the transpilePackages list in the next.config.js file. I even experimented with using the dynamicIconImports object with a fixed name (ignoring the name prop), but none of these approaches seemed to help.

My solution to the problem was to rewrite the use of the next/dynamic function for importing the 'lucide-react' module and utilize the promise result to obtain the necessary icon. This restored the development server to its normal performance.

image

I believe that the way the dynamicIconImports feature is implemented causes the NextJS development server to read and process all the imports when it shouldn't be doing so during compilation.

I am planning to submit a pull request to enhance the consumption of Lucide.icons by introducing a simple IconsName type. Additionally, I will revise the example of dynamic usage within the NextJS environment to eliminate the use of the dynamicIconImports object.

Would anyone like to contribute to this issue I'm addressing? Your contributions would be greatly appreciated. Thank you!

Steps to reproduce

  1. Begin a NextJS 13 project following the instructions in the documentation (using the latest version - 13.5.3).
  2. Develop an Icon component as outlined in the lucide-react documentation, employing the 'dynamic' feature from NextJS and 'dynamicIconImports' from lucide-react.
  3. Import this component into a page and take note of the increased compilation time.

Checklist

douglasrcjames commented 9 months ago

This is happening to me as well, and this lag may be connected to my icons "flashing" because it takes a bit longer to load, which is not an ideal UX! Seems to be on Production and Development environments.

O-Q commented 8 months ago

according to the next.js document, it may be related to this: dynamic() can't be used inside of React rendering as it needs to be marked in the top level of the module for preloading to work, similar to React.lazy.

douglasrcjames commented 8 months ago

according to the next.js document, it may be related to this: dynamic() can't be used inside of React rendering as it needs to be marked in the top level of the module for preloading to work, similar to React.lazy.

You linked the "Pages" directory for Next.js <v13, and not sure if this applies to the app structure of Next.js 13+, but I tried poking around and still couldn't get to work. If anyone can get dynamic icon importing to work without slowing down pages tremendously, I would love to figure this out!

kisstamasj commented 7 months ago

i have the same issue 🫤 when i run in production environment does not slow, but the icons are flashing when the page re rendering

gropaul commented 7 months ago

Same issue here ✋

gracefullight commented 7 months ago
Screenshot 2023-11-13 at 8 51 23 AM

I discovered that the lucide-react module, listed in transpilePackages within my next.config.js, is consuming approximately 9MB. This large footprint appears to be impacting the node.js performance adversely.

To provide some context, my current configuration utilizes dynamicImport, which I presumed would help in optimizing the load times and overall performance. However, with the substantial size of lucide-react, I'm not observing the expected efficiency.

I'm contemplating disabling dynamicImport as a potential solution to this issue. However, before proceeding with this change, I wanted to reach out to the community for insights and recommendations.

Here are my specific questions:

  1. Have others experienced similar issues with large module sizes in transpilePackages affecting performance?
  2. Is turning off dynamicImport a viable solution in this scenario, or are there alternative approaches that I should consider?

Thank you.

hannesuw commented 7 months ago

I'm facing the same issue. It took more than 10 seconds on development mode to compile a page while using Dynamic Icon.

morrisseybr commented 7 months ago

Any one trying the new exemple I make for using it with NextJS?

Please try and send me feedback, I will try to help you guys. For me the new implementation works like a charm.

MrVibe commented 5 months ago

I had an infinite loading issue with dynamic imports ⨯ node_modules/lucide-react/dynamicIconImports.js:1395 export { dynamicIconImports as default }; ^^^^^^

murfidaz commented 5 months ago

hello, how do I use it? because I save the icon name in the database, is there an example?

IanMirandaDev commented 4 months ago

Same issue here. I have 5k modules for each compilation in hotreload what tooks 40 secs to load.

I'm using the versions: "lucide-react": "^0.321.0" "next": "14.1.0"D

image

dawidseipold commented 4 months ago

Any one trying the new exemple I make for using it with NextJS?

Please try and send me feedback, I will try to help you guys. For me the new implementation works like a charm.

Sure it worked fine and removed the lag. But any idea on how to remove the unnecessary reloading of the icons on every page redirect?

ericfennis commented 4 months ago

Sorry for the late reply, I was a bit busy with other issues and PRs. I'm not sure how to fix this issue. This is one of the caveats of using dynamic imports and this huge object with dynamic improts. Each dynamic import is linked to a separate module. So when starting a dev server or starting the build process each module declared in the dynamicIconImports object will go through the compiler because each module can be "possibly" used. The compiler doesn't know which icons are passed through the dynamic import object.

So you maybe have 12 icons in you database this object still imports all 1400+ icons on build time. But will be dynamically loaded.

So there is no easy fix.

Also the suggestion and docs change from @morrisseybr is not really a fix, this is just moving everything from lucide-react to a separate module. In this case using it this way is even more preformant:

import { LucideProps, icons } from 'lucide-react';

interface IconProps extends LucideProps {
  name: keyof typeof icons;
}

const Icon = ({ name, ...props }: IconProps) => {
  const LucideIcon = icons[name];

  return <LucideIcon {...props} />;
};

export default Icon;

Note: This only recommended when using the app router with React Server components, otherwise all icons will be shipped to the frontend as well.

So we could create a separate sections in the docs about the app router and pages router.

weiying-chen commented 4 months ago

Note: This only recommended when using the app router with React Server components, otherwise all icons will be shipped to the frontend as well.

So what if one wants to use Lucide icons using the app router with React client components (with a component similar to the one you shared)?

kisstamasj commented 4 months ago

I use like this: https://lucide.dev/guide/packages/lucide-react#icon-component-example and its works just fine. I don't know what is your use case, but the dynamic import is useful if you want to make an icon selector component for a cms.

weiying-chen commented 4 months ago

Isn't that the same implementation @ericfennis shared?

He said:

Note: This only recommended when using the app router with React Server components, otherwise all icons will be shipped to the frontend as well.

app-rc commented 3 months ago

Here is my client component, and works fine

'use client'

import { memo } from 'react'
import dynamic from 'next/dynamic'

const Icon = memo(({ name, ...props }) => {

    const LucideIcon = dynamic(() =>
        import('lucide-react').then((mod) => {
            return mod[name] || mod['Ban']
        })
    )

    return <LucideIcon {...props} />
})

Icon.displayName = 'Icon'

export default Icon
ericfennis commented 3 months ago

@weiying-chen Indeed. @app-rc Your code example will ship all icons to the frontend. This will affect your bundle size. I hope you're aware of this.

SamuelOgbonnaeze commented 1 month ago

Just saved me a lot of stress with this solution. thank you

IonVillarreal commented 1 week ago

Hello, any news on this problem?