DEV Community

loading...

Shared components in a Lerna / TypeScript / Next.js project

alexbh profile image Alex ・2 min read

xpost from my personal blog https://alexbh.dev/posts/2021-01-03-nextjs-lerna-common

As you may have seen from my last post I'm migrating things to Next.js and keep running into little issues, so I've decided to blog about them here. The newest issue I've run into has been sharing common TypeScript components between apps in a Lerna monorepo. My setup looks like this:

packages/
  frontend/
  landing/
  ui/
  common/
  backend/
Enter fullscreen mode Exit fullscreen mode

What I've been trying to do it share a ui component between the frontend and landing projects. It's as simple as it sounds so I expected it to just work but there are some tricky gotchas. My landing page in this case is https://portabella.io and my frontend is https://app.portabella.io.

Issues

Cannot import outside of base directory

This error message looking familiar?

You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.
Enter fullscreen mode Exit fullscreen mode

The issue stems from the face that Next.js cannot import anything from outside its root, there are ways to change your tsconfig.json baseUrl but I couldn't get that way to work with Lerna.

Multiple React versions

Having issues with multiple versions of React? If this error is present in your application continue reading.

hooks can only be called inside the body of a function component
Enter fullscreen mode Exit fullscreen mode

Having to compile .tsx files before importing

The last issue I've been having is I needing to compile any .ts or .tsx files I want to import, however in a monorepo that's what I'm looking to do.

Links

If you've run into the problems above you may have found the following links:

If any of these sound familiar you're in luck! I've managed to cobble together enough things to make it work.

Solution

Pin React version

The first step is to settle on a React version you want to use in your monorepo. If you're using the latest Next.js it probably installed React 17, however my application is still on 16. Decide on one to use and update your package.json files accordingly.

Transpile .ts and .tsx files

Add this to your next.config.js to force Next.js to transpile your components:

config.module.rules.push({
  test: /\.tsx?|\.ts?$/,
  use: [options.defaultLoaders.babel],
});
Enter fullscreen mode Exit fullscreen mode

Resolve the correct React implementation

Additionally to the above Webpack config, add this to your next.config.js file:

config.resolve.alias['react'] = path.join(
  __dirname,
  '..',
  '..',
  'node_modules',
  'react'
);
config.resolve.alias['react-dom'] = path.resolve(
  __dirname,
  '..',
  '..',
  'node_modules',
  'react-dom'
);
Enter fullscreen mode Exit fullscreen mode

We're going up two directories in this case because we want our root node_modules/ directory and my folder structure has everything under packages/.

Hopefully that clears up any issues you were having! Next.js has some great features and generally works well but doing things like this is a bit finnicky, hopefully support is ironed out soon.

Discussion (4)

pic
Editor guide
Collapse
frontvu profile image
Jason Matthews

Did you find a workaround for the problem of

Cannot import outside of base directory
Enter fullscreen mode Exit fullscreen mode
Collapse
alexbh profile image
Alex Author

That's probably because you're trying to import {} from '../../common/';

If you're using Lerna you need to let it bundle the raw .ts{x} files into your node_modules. Then do import {} from @project/common.

Collapse
ajonp profile image
Alex Patterson

I actually just did the same last night, happy to see someone covering it. Are you as nervous as I am aboutnext-transpile-modules changing and not working in the future?

Collapse
alexbh profile image
Alex Author

Haha I guess I wasn't nervous because I hadn't thought about that.

I'll have to update this blog post with my current setup because it's not exactly the same as what I described here anymore.