Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Tree shaking doesn't work with Typescript barrel files #12557

Closed
Tracked by #63
majelbstoat opened this issue May 6, 2020 · 80 comments
Closed
Tracked by #63

Tree shaking doesn't work with Typescript barrel files #12557

majelbstoat opened this issue May 6, 2020 · 80 comments
Assignees
Labels
good first issue Easy to fix issues, good for newcomers TypeScript Related to types with Next.js. Webpack Related to Webpack with Next.js.

Comments

@majelbstoat
Copy link

majelbstoat commented May 6, 2020

Bug report

I originally raised this as a discussion, but now I think it's a bug.

Describe the bug

When using a barrel file to re-export components from a single location, tree-shaking does not function correctly.

To Reproduce

I'm using Next 9.3.6 and I've arranged my components like:

  components/
    Header/
      Header.tsx
    Sidebar/
      Sidebar.tsx
    index.ts

Each component file exports a single component, like this:

export { Header }

index.ts is a barrel file that re-exports from each individual component file:

  export * from './Header/Header.tsx'
  export * from './Sidebar/Sidebar.tsx'
  // ...

I then use a couple of components in _app.tsx like:

import { Header, Sidebar } from "../components"

There's about 100 components defined, and only a couple are used in _app.tsx. But when I analyze the bundle I have a very large chunk, shared by all pages, and it contains all my components, resulting in an inflated app page size:

Screenshot 2020-05-06 09 35 32

I use a similar import strategy within pages, and every one of them appears to contain every component.

my tsconfig.json is:

  "compilerOptions": {
    "allowJs": true,
    "baseUrl": ".",
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "preserveConstEnums": true,
    "removeComments": false,
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "es5"
  }

and I'm using next's native support for the baseUrl field. I haven't changed the module or the target.

When I change the _app.tsx imports to:

import { Header } from "../components/Header/Header"
import { Sidebar } from "../components/Sidebar/Sidebar"

the common bundle and app page size drops dramatically, as I would expect it to:

Screenshot 2020-05-06 09 34 21

Expected behavior

The app page size should be the same using both import strategies.

System information

  • OS: [e.g. macOS]
  • Version of Typescript: [e.g. 3.8.3]
  • Version of Next.js: [e.g. 9.3.6]
@timneutkens timneutkens added good first issue Easy to fix issues, good for newcomers help wanted labels May 25, 2020
@timneutkens
Copy link
Member

Feel free to investigate and solve

@rahul3103
Copy link

rahul3103 commented May 28, 2020

#13490

Same happening with jsx also.
version: 9.4.2

@Timer Timer removed the help wanted label Jun 3, 2020
@seeekr
Copy link

seeekr commented Sep 10, 2020

Has anyone been able to solve this? In one of our projects we seem to be getting no tree shaking at all for any TS application code, only for third party library code.

Alternatively, could any of the maintainers provide some hints as to how one would best go about investigating and solving such an issue?

EDIT: For posterity, the problem in our case was that the bundle analyzer's output is harder to interpret correctly than it appears, which we failed to do initially. It seems that tree shaking was not the problem.

@stevethatcodes
Copy link

Has anyone been able to solve this? In one of our projects we seem to be getting no tree shaking at all for any TS application code, only for third party library code.

Alternatively, could any of the maintainers provide some hints as to how one would best go about investigating and solving such an issue?

@seeekr We actually ended up just removing our barrel files.

I did a simple experiment with Webpack and a couple of JavaScript modules + a barrel file (no Next, or Typescript) and the same behaviour was present with limited options to enable tree shaking to occur.

If you have no side effects in your code then you may be able to look into the Webpack side effects setting to allow this to work for you, but as soon as you're importing a module classed as a side effect, you may start to notice issues. For us, our polyfill imports stopped working along with a whole host of other stuff.

I'm not sure this is something Next can/should solve though, as it seems to be inherent to Webpack

@VWSCoronaDashboard8
Copy link

VWSCoronaDashboard8 commented Sep 22, 2020

@stevethatcodes @majelbstoat Tree shaking seems to work for me as long as I specify side effects in package.json:

"sideEffects": [
    "./src/some-side-effectful-file.js"
  ]

This is according to the Webpack docs linked above. I think you would just have to determine where your polyfills and other side effects are happening and add those files to the side effects list.

@stevethatcodes
Copy link

@VWSCoronaDashboard8 Yeah, that's an approach we tried, though still had issues with the polyfills not loading. In fairness, we took the approach to remove barrel files as the path of least resistance, as they didn't add much value and we didn't need to do anything else to our Next/Webpack setup to bring back tree-shaking.

@RobbyUitbeijerse
Copy link

@sphilee I don't actually think it works / resolves the issue, I've installed Webpack 5 on my current Next.js projects but it doesn't have any positive impact on the bundle size, at least for barrel files structured as the issue example by @majelbstoat.

@VWSCoronaDashboard8
Copy link

@RobbyUitbeijerse @sphilee I have concluded the same. Webpack 5 didn't solve this issue for me.

@RobbyUitbeijerse
Copy link

RobbyUitbeijerse commented Dec 15, 2020

Based on the following issue webpack/webpack#11821 , Webpack 5 should actually eliminate dead code with the minimize option being enabled (which is the case by default for next build). Let's assume that it does (I haven't actually verified it myself) -

What I'm concerned about is that it might actually work for the shared bundle which is loaded for all pages, but doesn't actually consider the page by page chunks, meaning that while dead code is actually eliminated from the shared bundle - you are still left with a single bundle containing everything instead for smaller chunks per page that only contain what you need for that page. Quite unsure whenever it's possible to actually resolve that or that changing the imports is the only way to go.

@RobbyUitbeijerse
Copy link

@timneutkens Webpack is not my strong suit, but do you have any clue if my comment above makes any sense in terms of what we are seeing?

@kelvinlouis
Copy link

kelvinlouis commented Jan 18, 2021

I had similar issues in the past when using export * from 'xxx';. Have you tried explicitly re-exporting your named and default exports in the barrel files?

In the past we also had issues with babel when it came to exporting types. Since then we started exporting types explicitly using export type.

// index.ts
export {
  default as Header,
  HeaderX,
  HeaderY,
} from './Header/Header.tsx';

export type {
  Props as HeaderProps,
  AdditionalHeaderTypeX,
} from './Header/Header.tsx';

export {
  default as Sidebar,
  SidebarX,
  SidebarY,
} from './Sidebar/Sidebar.tsx';

export type {
  Props as SidebarProps,
  AdditionalSidebarTypeX,
} from './Sidebar/Sidebar.tsx';

@RobbyUitbeijerse
Copy link

RobbyUitbeijerse commented Feb 3, 2021

I actually started doing the same recently:

image

But it turns out, no luck:

image

What I did find tho, that when I put all of the components in an external library and build that library using Rollup while preserving the modules, tree shaking of the same components seems to work properly, meaning that it should be possible to get it all working one way or the other.

[edit] Same result with webpack 5

@nandorojo
Copy link

nandorojo commented Apr 8, 2021

I actually started doing the same recently:

image

Same experience here. I was pulling my hair out trying to figure this out. I have a monorepo, and in the index.ts of each package, I was re-exporting my components. Not only did this break tree shaking: it also made yarn next dev take like a minute longer to load. Changing my imports directly to the root file of the components solved this. Wish I'd known this sooner – I have a lot of imports to change.

I can make a repro in a monorepo this weekend.

I'm using Next 10.1.4-canary.2 and webpack 4, for context.

@JoeyFenny
Copy link

This has been open for over a year. Has anyone found a solution yet? Using a barrel file results in my bundle reaching 12mb whereas without I'm less than 1mb

@R-Bower
Copy link

R-Bower commented Jun 5, 2021

@JoeyFenny
I managed to get this working by setting "sideEffects": false in the package.json of the package with the barrel export. You may also want to check this comment: #12557 (comment)

@jrmyio
Copy link

jrmyio commented Jun 5, 2021

I am running into simular problems in a non next.js project. Upgrading from webpack 4 to webpack 5 did not fix it, sideEffects: "false" in the package.json does enable tree shaking but the barrel pattern still fails.

I think this is a webpack problem, maybe we should move the dicussion there?
Though it would really help if we got a minor example repository that shows the problem.

Important edit: It seems I was wrong that tree shaking wasn't fully working in my webpack project because of the barrel pattern. The barrel pattern (vs direct importing) made a lot more code be processed which increased the chance of tree shaking not being applied because of sideEffects within that code.

I don't know OP's exact case or next.js's handling when it comes to tree shaking vs webpack 5 but I did came accross a blog post that goes more in-depth on tree shaking that I would recommend reading: https://dev.to/livechat/tree-shaking-for-javascript-library-authors-4lb0

In the end I had to add /*#__PURE__*/ to some function calls to let tree shaking be applied correctly.

Again, I don't know if OP has the same issue, or there is some config issue or if it is next.js that is failing, but what I did found out is that webpack 5 does support the barrel pattern.

@JoeyFenny
Copy link

settings sideEffects to false and adding /*#__PURE__*/ to the function call did not work either. But I agree that this discussion should perhaps be moved to webpack

@b2rsp
Copy link

b2rsp commented Jun 14, 2021

We dont have any news or ideas about this? I am really struggling to fix this since its having a big impact in our page load.

@sokra
Copy link
Member

sokra commented Jun 21, 2021

For tree-shaking to work with a barrel file you need

  • flag the file and/or the child files as side effect free ("sideEffects": false in package.json)
  • build in production mode webpack 4 or 5
  • not bundle the library (it must be in separate files for sideEffects to work)
  • Alternatively to side effect free flagging you can use /*#__PURE__*/ to carefully flag statements that might be considered as having side effects. If you do that, you must minimize to file to see the effect. This is very tricky to get right

MarcoMadera added a commit to MarcoMadera/Blog that referenced this issue Aug 23, 2021
sideEffects: false, makes tree shaking work with barrels files vercel/next.js#12557
Mapping only needed data from posts
Change first render tweet info date
@nandorojo
Copy link

not bundle the library (it must be in separate files for sideEffects to work

@sokra I want to have barrel files as the main file for each of my monorepo's packages. If I am adding these monorepo packages to next-transpile-modules, would that constitute "bundling" the library?

@JEGardner
Copy link

I'm sorry but I can't believe that this issue has been opened for more than 3 years now and that there is still no solution 🤷‍♂️

I have to agree. All these micro-optimisations and then you have this which would probably save 100s of kb being sent on every route on most nextjs apps. Don't get it.

@enchorb
Copy link

enchorb commented Oct 6, 2023

@leerob - any ETA on a solution for this?

@Slowl
Copy link

Slowl commented Oct 8, 2023

I'm so happy I decided to make the changes early even if it took me some time to do it. It would have taken me more time today.

Crazy to see this is still not resolved.

Next step might be to move the project to Astro 🤔

@gajus
Copy link

gajus commented Oct 10, 2023

Randomly stumbled upon this, but I highly recommend ESLint rule that restricts use of barrel files

https://github.com/gajus/eslint-plugin-canonical#no-barrel-import

It is auto-fixable so you don't need to do much to leverage it and it will prevent bloat of your bundle size.

@robert-orlinski
Copy link

robert-orlinski commented Oct 19, 2023

Hmm, either something was fixed or I don't understand.

I have set up very simple Next.js app using create-next-app (using Next 13.5.6 with pages directory) and created 5 files:

First file (/components/OneComp.tsx):

export const OneComp = () => {
  console.log('first');

  return <div>OneComp</div>
}

Second file (/components/CoupleComp.tsx):

export const Comp1 = () => {
  console.log('fst');

  return <div>OneComp</div>
}

export const Comp2 = () => {
  console.log('sec');

  return <div>OneComp</div>
}

export const Comp3 = () => {
  console.log('thrd');

  return <div>OneComp</div>
}

Third file (/components/Sub/SubComp.tsx):

export const SubComp = () => {
  console.log('sub');

  return <div>OneComp</div>
}

Nested barrel file (/components/Sub/index.ts):

export * from './SubComp'

And the last one - main barrel file (/components/index.ts):

export * from './CoupleComp'
export * from './OneComp'
export * from './Sub'

It looks like this in the end:

image

Then I have imported and used 2 from 5 components imported from the main barrel file into _app.tsx file (similar scenario to the one provided by the author on the whole thread):

import { OneComp, Comp3 } from '@/components'
import '@/styles/globals.css'

export default function App() {
  return (
    <>
      <OneComp />
      <Comp3 />
    </>
  )
}

...and the end file generated after running npm run build and then npm start contains ONLY imported component's code.

This is the content of generated /_next/static/chunks/pages/_app-cfb55d8006062ffe.js file:

(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[888],{6840:function(n,e,_){(window.__NEXT_P=window.__NEXT_P||[]).push(["/_app",function(){return _(3564)}])},3564:function(n,e,_){"use strict";_.r(e),_.d(e,{default:function(){return App}});var c=_(5893);let Comp3=()=>(console.log("thrd"),(0,c.jsx)("div",{children:"OneComp"})),OneComp=()=>(console.log("first"),(0,c.jsx)("div",{children:"OneComp"}));function App(){return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(OneComp,{}),(0,c.jsx)(Comp3,{})]})}_(415)},415:function(){}},function(n){var __webpack_exec__=function(e){return n(n.s=e)};n.O(0,[774,179],function(){return __webpack_exec__(6840),__webpack_exec__(9974)}),_N_E=n.O()}]);

As you can see, it only contains code of OneComp and Comp3 which were imported. You can easily double check that by searching for console.log instances.

Anticipating questions: only this file contains code related to components created by me.

Is there something I don't understand here or what I see here is tree shaking in action? 🤔

@r34son
Copy link
Contributor

r34son commented Oct 22, 2023

Is this issue now fixed with Optimized Package Imports? #54695

Yeah, seems like it helps. I`ve tested with @nextui-org/react and many of internal packages was gone

@jorenbroekema
Copy link

jorenbroekema commented Dec 3, 2023

For those who have hypothesized that it's a Webpack issue, I cannot reproduce the issue in Webpack alone, treeshaking barrel files seems to work fine there: https://replit.com/@JorenBroekema/barrel-treeshaking

In fact, I've also tested this in Rollup and esbuild and they can also treeshake it just fine. Parcel/Browserify not so much

So seems like a NextJS specific problem.

Correction: Parcel's treeshaking is built into it's minification thing, so if you run it with minification on, it'll treeshake just fine as well.

@samcx
Copy link
Member

samcx commented Jan 3, 2024

Hi everyone!

Please let us know if https://nextjs.org/docs/app/api-reference/next-config-js/optimizePackageImports resolves your issue with regards to proper tree shaking with Typescript barrel files. If yes, we will proceed with moving this issue to :nextjs: Discussions.

Happy 2024!

@ivan-kleshnin
Copy link
Contributor

Please let us know if https://nextjs.org/docs/app/api-reference/next-config-js/optimizePackageImports resolves your issue with regards to proper tree shaking with Typescript barrel files. If yes, we will proceed with moving this issue to Discussions.

The very idea that we have to manually list libraries that were previously tree-shaken automatically is... not inspiring.

@samcx
Copy link
Member

samcx commented Jan 3, 2024

@ivan-kleshnin Can you share a :repro: showcasing where it was done automatically previously so we can take a closer look?

@gajus
Copy link

gajus commented Jan 3, 2024

@ivan-kleshnin out of curiosity, why did you thumbs down ESLint plugin I suggested?

#12557 (comment)

It is working great for us

@jorenbroekema
Copy link

Regardless of whether it was or wasn't done automatically previously, can you explain why it makes sense to manually specify a list of package to optimize? Isn't it always a good default to optimize dependencies? From the docs, it is not clear in what scenario it is helpful to "not optimize packages".

From what I can tell, the default should be to optimize/treeshake all dependencies, at least for production, with perhaps an opt-out, although I don't see a reason for that either. Again, maybe I'm missing some context but this seems to me like really awful API design when considering the principle of "choose good defaults" and "allow users to opt into necessary exceptions".

@jorenbroekema
Copy link

@ivan-kleshnin out of curiosity, why did you thumbs down ESLint plugin I suggested?

#12557 (comment)

It is working great for us

I'm not the downvoter but couple of reasons:

  1. You're linting plugin fixes barrel files, great, but that only helps for my dependencies exporting barrel files if I let ESLint lint my dependencies, and then it fixes my dependencies code which I'm not happy to commit to git, personally
  2. A linting plugin as a solution to the problem that Next's default behavior with regards to treeshaking barrel files sucks is not really a good solution, it's a workaround. Maybe a useful one at that, but I get the downvote, I personally don't see it as an acceptable workaround as it introduces another devdependency -> arguably tech debt.

@ivan-kleshnin
Copy link
Contributor

ivan-kleshnin commented Jan 4, 2024

@ivan-kleshnin out of curiosity, why did you thumbs down ESLint plugin I suggested?
#12557 (comment)
It is working great for us

Because that plugin simply "Restricts the use of barrel files". I don't see barrel files as something bad that should be avoided, me/my teams in the present/past heavily used them outside of NextJS context, with great success. They are convenient, and I'm not aware of any cons, except that they cripple NextJS... So why I should lint myself against NextJS issue? 🤔 If they aren't going to fix... ok, we have a growing list of complaints against this framework and React anyway, might be a good final reason to switch out.

@IonelLupu
Copy link

IonelLupu commented Jan 4, 2024

I came across this issue when using react-aria-components. When using just a simple <Button>, the entire react-aria-components library is imported.

While reading this thread, I saw this issue linked above: #44039

It seems when using use client in your page, makes the builder tree-shake correctly. Not sure why is that. Buuuut, the issues don't stop there. If you use the <Button> on page A and the <Select> on page B, the bundle analyzer shows page A imports the <Select> component EVEN if page A doesn't use the <Select> component. It seems it combines all of the react-aria-components files into one big import in EVERY SINGLE PAGE.

I detailed the issue here: #60206

@samcx
Copy link
Member

samcx commented Jan 4, 2024

...They are convenient, and I'm not aware of any cons

@ivan-kleshnin As far as I'm aware, there is a cost to using barrel files, which is why we recently introduced optimizePackageImports.

I agree that it is not ideal to manually have to add packages to optimizePackageImports if the package in question is not already manually listed here. →

...new Set([
...userProvidedOptimizePackageImports,
'lucide-react',
'@headlessui/react',
'@headlessui-float/react',
'@heroicons/react/20/solid',
'@heroicons/react/24/solid',
'@heroicons/react/24/outline',
'@visx/visx',
'@tremor/react',
'rxjs',
'@mui/material',
'recharts',
'@material-ui/core',
'react-use',
'@material-ui/icons',
'@tabler/icons-react',
'mui-core',
// We don't support wildcard imports for these configs, e.g. `react-icons/*`
// so we need to add them manually.
// In the future, we should consider automatically detecting packages that
// need to be optimized.
'react-icons/ai',
'react-icons/bi',
'react-icons/bs',
'react-icons/cg',
'react-icons/ci',
'react-icons/di',
'react-icons/fa',
'react-icons/fa6',
'react-icons/fc',
'react-icons/fi',
'react-icons/gi',
'react-icons/go',
'react-icons/gr',
'react-icons/hi',
'react-icons/hi2',
'react-icons/im',
'react-icons/io',
'react-icons/io5',
'react-icons/lia',
'react-icons/lib',
'react-icons/lu',
'react-icons/md',
'react-icons/pi',
'react-icons/ri',
'react-icons/rx',
'react-icons/si',
'react-icons/sl',
'react-icons/tb',
'react-icons/tfi',
'react-icons/ti',
'react-icons/vsc',
'react-icons/wi',
]),
]

Ideally, we just automatically detect packages that need to use optimizePackageImports, so the user doesn't have to even concern themselves with this. Will be discussing this with the team to see how we can achieve that!

@IonelLupu
Copy link

...They are convenient, and I'm not aware of any cons

@ivan-kleshnin As far as I'm aware, there is a cost to using barrel files, which is why we recently introduced optimizePackageImports.

I agree that it is not ideal to manually have to add packages to optimizePackageImports if the package in question is not already manually listed here. →

...new Set([
...userProvidedOptimizePackageImports,
'lucide-react',
'@headlessui/react',
'@headlessui-float/react',
'@heroicons/react/20/solid',
'@heroicons/react/24/solid',
'@heroicons/react/24/outline',
'@visx/visx',
'@tremor/react',
'rxjs',
'@mui/material',
'recharts',
'@material-ui/core',
'react-use',
'@material-ui/icons',
'@tabler/icons-react',
'mui-core',
// We don't support wildcard imports for these configs, e.g. `react-icons/*`
// so we need to add them manually.
// In the future, we should consider automatically detecting packages that
// need to be optimized.
'react-icons/ai',
'react-icons/bi',
'react-icons/bs',
'react-icons/cg',
'react-icons/ci',
'react-icons/di',
'react-icons/fa',
'react-icons/fa6',
'react-icons/fc',
'react-icons/fi',
'react-icons/gi',
'react-icons/go',
'react-icons/gr',
'react-icons/hi',
'react-icons/hi2',
'react-icons/im',
'react-icons/io',
'react-icons/io5',
'react-icons/lia',
'react-icons/lib',
'react-icons/lu',
'react-icons/md',
'react-icons/pi',
'react-icons/ri',
'react-icons/rx',
'react-icons/si',
'react-icons/sl',
'react-icons/tb',
'react-icons/tfi',
'react-icons/ti',
'react-icons/vsc',
'react-icons/wi',
]),
]

Ideally, we just automatically detect packages that need to use optimizePackageImports, so the user doesn't have to even concern themselves with this. Will be discussing this with the team to see how we can achieve that!

@samcx optimizePackageImports doesn't work in my case when using react-aria-components. I detailed the issue in the comment above. Using Vite works correctly. Also, using "use client" kinds gives okish results, but there is a bigger problem when you have multiple pages. Again? Detailes in the discussion I listed above

@samcx
Copy link
Member

samcx commented Jan 5, 2024

@IonelLupu Thank you for re-iterating your issue involving react-aria-components and creating a detailed discussion post. I have created #60246 from your discussion post and will be taking a look!

@samcx
Copy link
Member

samcx commented Feb 21, 2024

Update: there was a fix for the above issue that entailed importing direct Client Components in Server Components.

#60246 (comment)

@tocosastalo
Copy link

tocosastalo commented Mar 19, 2024

Maybe this isn't related, but I came across this issue when using plaiceholder, which is supported only in server components.

I have barrel/index file which re-exports both server and client "elementary" components. During development when some page imports client component from the barrel file, the app crashes on error "ReferenceError: require is not defined". So it looks like the server component is automatically imported/loaded and gets into the browser despite the fact that it is marked as server component (without "use client" at the top) and is not used anywhere on the page.

Simplified components are:

// ServerImageWithBlur.tsx
import { getPlaiceholder } from "plaiceholder"

export const ServerImageWithBlur = async (props: ImageProps) => {
  const { base64, img } = await getPlaiceholder(props.src)
  return <Image placeholder="blur" blurDataURL={base64} {...props} {...img} />
}
// Navbar.tsx
"use client" 

export const Navbar = () => {
  return <div>...</div>
}
// index.ts
export { ServerImageWithBlur } from "./ServerImageWithBlur"
export { Navbar } from "./Navbar"

Error:

Unhandled Runtime Error
ReferenceError: require is not defined

When I comment the re-export of the server component from index.ts, the application works.

// index.ts
// export { ServerImageWithBlur } from "./ServerImageWithBlur"
export { Navbar } from "./Navbar"

@samcx
Copy link
Member

samcx commented Mar 19, 2024

@tocosastalo Can you create a new separate bug report so we can take a look?

In the meantime, I will be moving this over to Next.js discussions, since we have veered from the original issue posted back in 2020.

@vercel vercel locked and limited conversation to collaborators Mar 19, 2024
@samcx samcx converted this issue into discussion #63494 Mar 19, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
good first issue Easy to fix issues, good for newcomers TypeScript Related to types with Next.js. Webpack Related to Webpack with Next.js.
Projects
None yet
Development

No branches or pull requests