Migrating a CRA(ejected) app to Next.js v12

I recently got the chance to migrate a custom CRA app to Next.js for a client. There were around ~4000 modules that were involved directly or indirectly(as npm dependency). This is what I learned through the process.

Approach to Adopting Next.js (Step-1)

With a custom CRA set up the only suitable approach is the incremental adoption. But you would have to choose among the following two strategies.

  • Subpath - CRA has the main control and Next.js tackles only a path
    • Any new page addition would happen through react-router only by default.If you need to do it through Next.js, you would have to change it’s path.
  • Rewrites - Next.js has the main control and it tells CRA what it will do.
    • Any new page addition would happen to Next.js

It should be easy to switch b/w either of the two approaches by modifying Next.js. I would suggest a hybrid strategy.

  • Go for the Subpath approach in the POC stage as it is pretty quick to do
  • Switch to the Rewrite approach when you are ready to ship to production

Getting components to work with Next.js(Step-2)

1. Dependencies Changes

  • Babel has been replaced by SWC by default in the latest version. The app had a babel config, which Next.js complained about. Next.js fallbacks to using Babel instead of SWC if it discovers a babel config in the app.
    • Do update babel and babel plugins to the latest version.
  • Next.js v12 enforces webpack v5. There used to be a flag [webpack5](https://nextjs.org/docs/messages/webpack5) that you can use to disable enforcing the use of webpack v4, which has been removed in Next.js v12
    • It would mean installing the latest version of all the plugins that are compatible with v5 if you still want to use those plugins.
    • You would have to go and fix the data input format of a few plugins as that might not be compatible with v5. Webpack does a decent job of telling what exactly is wrong in plugin config.
  • If you see an error that Webpack can’t understand the SVG file. Add SVG loader in webpack

2. App Code Changes

  • If you are importing a CSS with side-effect import "a.css", it is global CSS and Next.js throws an error and asks to move it to _app.js. Learn how to do it here. If there are many such imports it’s going to be a pain to find out all of them
    • Run next dev and see where it is reporting this error and move all such imports to _app.js
      • It gives errors one by one which is sad and thus it takes a lot of time depending on how many such files are there. You might be better looking at a pattern as to where all such imports exist and make the changes according to that pattern in one go.
      • If you would find a lot of such instances, you can comment those CSS Import lines temporarily to focus on other problems first and then come back to convert those CSS files into CSS modules.
    • You should plan to convert all those global CSS imports to CSS modules anyway.
  • React Router related changes. Also, read https://nextjs.org/docs/migrating/from-react-router
    • useLocation provided by react-router has to be replaced with useRouter provided by next/router.
    • Link component provided by react-router is to be replaced with Link component provided by next/link
      • Also, to attribute has to be converted to href attribute.

    Some of the Errors that I encountered and how I fixed them

    • You may need an additional loader to handle the result of these loaders. export type { CCProviders } from "./types";
      • Solution: Upgrade the babel plugins and babel to the latest version.
    • Module not found: Can't resolve 'react/jsx-runtime' in ...
      • Solution: Make sure Typescript is at least 4.3.2 and react is at least v16.4.0. Read this for more details
    • React Router’s Link component allows multiple children but the next/link doesn’t allow that.
      • Solution: Avoid having multiple children.

Pre-rendering (SSR or SSG)(Step-3)

At this point, you should have a working page that is rendered by Next.js. It might look to you that your job is done as you are seeing the page perfectly but it might or might not be pre-rendering. I

f you have a pretty simple app(with no data fetch requirements), then Next.js should be automatically rendering the components on the server-side and you are done. But even with a slightly complex app, you would face issues with getting components to render into HTML on the server.

This is owing to the following reasons:

  • useEffect never executes on the server. This is where the data fetching code usually exists.
  • State changes don’t cause components to re-render on the server.

You would have to choose the data fetching strategy according to https://nextjs.org/docs/basic-features/data-fetching(which also decides you are going to do SSR or SSG). After that, you would have to refactor your code let Next.js own data fetching.

... Loading comments