mantine: Cannot Modularize Imports for Next.js

What package has an issue

@mantine/core

Describe the bug

Hey there,

With Next.js >= 13.1, you can make use of their Modularize Imports feature.

Right now there is an open PR on material-ui mentioning it: https://github.com/mui/material-ui/pull/35457/files

Could mantine support that as well? I attempted it, and it fails compilation at import { useMantineTheme } from '@mantine/core';

What version of @mantine/hooks page do you have in package.json?

N/A

If possible, please include a link to a codesandbox with the reproduced problem

No response

Do you know how to fix the issue

None

Are you willing to participate in fixing this issue and create a pull request with the fix

None

Possible fix

No response

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Reactions: 8
  • Comments: 25 (9 by maintainers)

Most upvoted comments

@rtivital I made a demo app using create-react-app to profile this.

Here’s the demo app:

function App() {
  return (
      <MantineProvider withGlobalStyles withNormalizeCSS>
          <Button>Click me!</Button>
      </MantineProvider>
  );
}

export default App;

I will showcase the modules loaded by webpack in each import scenario.

Scenario 1: Recommended by docs

import { MantineProvider } from '@mantine/core';
import { Button } from '@mantine/core';
orphan modules 1.06 MiB [orphan] 454 modules
runtime modules 28.5 KiB 14 modules

Scenario 2: Reaching into the ESM directory manually

import { MantineProvider } from '@mantine/styles/esm/theme/MantineProvider';
import { Button } from '@mantine/core/esm/Button/Button';
orphan modules 12.1 KiB [orphan] 20 modules
runtime modules 28.5 KiB 14 modules

Notice 454 modules were loaded with the current approach, and only 20 modules were loaded with the desired approach.

While tree shaking will occur during the minification process, webpack still needs to traverse all of these modules that get imported. If @mantinedev’s build can be restructured so that:

  1. "module": "esm/index.js", is changed into "module": "index.js",
  2. All top level imports can be retrieved using path imports

Then transformations like Modularize Imports or babel-plugin-import will work, and we could import as we currently are:

import { Button, MantineProvider } from '@mantine/core';

while still only pulling in 20 modules.

@rtivital in order to increase page load times, since they can become very slow. @tabler/icons, the dependency for mantine, is also in the middle of it’s v2 branch for the same support: https://github.com/tabler/tabler-icons/issues/359

We are really suffering from this issue.

Screenshot 2023-05-04 at 13 44 08

Thanks for the clarification @anthonyalayo .

We are already using the public API by using the components the library publicizes. The only request is that the public API becomes consistent in the way that the file layout is structured so that we don’t need to load all modules.

IMHO that’s two different things.

  1. You’re using the public API described in the docs and made public by the root exports from each package. That’s the expected way to use the library.
  2. You want to access the same things via sub imports that follow a specific schema. So:
  • import {Button} from '@mantine/core'; may become import Button from '@mantine/core/Button';
  • import {useMantineTheme} from '@mantine/core'; may become import useMantineTheme from '@mantine/core/useMantineTheme';
  • As I read it using default exports isn’t mandatory, so it might be possible to stick with named exports if wanted.

Is my understanding correct? So to get this working and stable the file structure must become part of the public API - in my understanding it’s currently not part of it. And it must be consistent with the export namings.


Another idea: Because next.config.js is a JS file and not a JSON (or might it be a JSON file?) it may also be possible to utilize some require statements and fetch the transformation configuration directly from the package:

module.exports = {
  modularizeImports: {
    ...require('@mantine/core/nextJsModularizeImports.js'),
  },
}

This opens up the possibility to keep the file structure out of the public API.


And what about using package.json exports? https://webpack.js.org/guides/package-exports/ https://nodejs.org/api/packages.html#subpath-exports

Is this already usable? For me this looks like the better and more standardized approach.

I’ll put my experience here too: today it is not much about “dev” speed and module loading, but also about “React Server Components” experience, because when I’ll use @mantine/core, I’ll get a lot of stuff where Next.js complains that I should use “use client”; even when it’s not necessary: image

Just because of emotion cache from Mantine I’ve to put “use client”; in a place where it does not make much sense…

in my case optimizePackageImports does not help me. i have ben add @mantine/core and also @tabler/icons-react and have the same modules in dev mode. (and same build ms)

Compiled /in 9.2s (6432 modules) (4000+ modules only @tabler/icons-react) but i am use only 10 icons. that a very big problem… modularizeImports have same problem.

@cyantree

I’m using esbuild and it treeshakes Mantine just fine. And it “only” treeshakes on module level so unused modules will be discarded but unused exports within modules not (if I understand it correctly). And the build performance for dev/production is excellent.

Treeshaking vs module loading is different. Everyone agrees here that treeshaking is working, but all modules are being loaded during development. That is what we are looking to fix.

So if I understand it correctly these Next.js transform rules require the package to follow some specific file convention that the user can configure. Therefore the internal package/export structure becomes part of the public API. Is this really good?

We are already using the public API by using the components the library publicizes. The only request is that the public API becomes consistent in the way that the file layout is structured so that we don’t need to load all modules.

What exactly do you require?

I posted this in the issue description:

Could mantine support that as well? I attempted it, and it fails compilation at import { useMantineTheme } from > ‘@mantine/core’;

Just an update here that @tabler/tabler-icons just merged their support for this as well: https://github.com/tabler/tabler-icons/issues/359

So, why do you need tree shaking in development mode?