Converting a project from React to Next.js
Tags: react, nextjs, tutorial, frontend
Published: 2024-01-19 23:46:18

In this article, you will learn how transform a React app into a Next.js app.

So, while refactoring my portfolio, I decided to **upgrade it from React to Next.js**. In this article I will tell you how I did it, as I find it might be able to help you out. I found no specific orientation on Next.js [official docs](https://nextjs.org/docs) about how to do it, and just a few posts about it. So I figured probably the easiest and safest way, for me, would be to create a Next app and basically transfer my content to it - or transfer its configuration to my project.

But, before that, you could be asking yourself: 

### Why use Next.js?

Well, in my case, besides the facilities Next.js brings, I had a few other reasons to use it in my portfolio. Although it is a relatively simple website, I have been working with Next.js for a few months now and had no demo with it in my GitHub to show. Also I wanted to use Contentlayer as it is a way I can have my curriculum and all site contents written in markdown and easily manage the content and have my types generated for Typescript use. I couldn’t use Contentlayer with React since it is, at this moment, for Next.js applications only. Hence, I thought it would be interesting to upgrade my project to Next.js.

### And what does Next.js do?

Next.js is a flexible React framework that improves both developer and user experiences. About developer experience, by the way, Next.js has amazing documentation and tutorials, I highly recommend it! It makes it easy to do routing, loading and error states. It also has server-side and client-side rendering you can choose for each component, reducing the JS code to run on the client. I will tell more about Next.js features in _another post_.

Enought talking, let’s get going!

---


### The React project

I had a project in React (v18) deployed on ([Vercel](https://vercel.com/)). This is how the project folder structure  looked like:

![A screenshot showing the file structure of the React app. Folders are: 'docs', 'node_modules', 'public' and 'src' (containing folders named 'components', 'content', 'files', 'img' and files named 'App.css', 'App.js', 'App.test.js', 'index.css', 'index.js', 'reportWebVitals.js' and 'setUpTests.js'). Other files on the root of the project are '.gitignore', 'package-lock.json', 'package.json', 'README.md' and 'tailwind.config.js'.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n23tkw09gorrcxo8y04z.png)

### Migrating project to Next.js

So, first of all, I am **versioning my code with git**. If you are a beginner, I recommend you start getting familiar with code versioning as soon as possible. After the project has been initialised with git, I opened the terminal and created a new branch on my code to work on this modifications. This way I leave it separate from the main branch and have **full control on what I’m doing**.

```
git checkout -b nextjs
```

Then, on branch `nextjs`, I created a next app on the root of my project.

```
npx create-next-app@latest
```

Here you need to name your project. You can give any name you want, because we will transfer the files to the current project. For this example, I called it `my-app`. As I intend to use Typescript, Tailwind and ESLint, I added all of them on the installation, just by answering to the prompts on the terminal. Also used App router, did not add `src` folder and used the import alias `@/`. You can do as you wish.

Here are the instructions from the official docs, if you need more info on the installation: [https://nextjs.org/docs/getting-started/installation](https://nextjs.org/docs/getting-started/installation).

Then, a new folder was created inside my project folder with all the initial files from the Next app. The whole folder structure now looks like this:


![A screenshot showing the file structure of the React app after Next.js installation on the root of the project. It contains the same folders and files in the root; folders are: 'docs', 'node_modules', 'public' and 'src' (containing folders named 'components', 'content', 'files', 'img' and files named 'App.css', 'App.js', 'App.test.js', 'index.css', 'index.js', 'reportWebVitals.js' and 'setUpTests.js'), and files are '.gitignore', 'package-lock.json', 'package.json', 'README.md' and 'tailwind.config.js'. In addition, it contains a folder in the root of the project called 'my-app'. This folder contains folders 'app', 'node_modules' and 'public' and files '.eslintrc.json', '.gitignore', 'next-env.d.ts', 'next.config.mjs', 'package-lock.json', 'package.json', 'postcss.config.js', 'README.md', 'tailwind.config.ts' and 'tsconfig,json'.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jn3j74i8y8jcijuz82a6.png)

Note that inside the **root folder** I have the React files (`package.json`, `package-lock.json`, `.gitignore`…) and the **new Next.js project folder** with its files.

### Adjusting the files structure

Now, you need to define the configuration files of your project. Let’s start with `package.json`. I suggest you compare your original `package.json` with your Next app's `package.json` and copy to the original one what is missing. Replace the `"scripts"` with the scripts from the Next app, keep the dependencies and devDependencies you use and add the ones from the Next app to your files.

> _Important: My first attempt was erasing and replacing `package.json`, `package-lock.json` and `.gitignore`, but I had some **problems with git** versioning due to it. So I suggest replacing the content of these files instead of deleting the file and pasting the new one. I consider it a better idea also because you use the opportunity to read the files and keep the dependencies you are using, so you don’t have to install them again._

Do the same with `.gitignore`. See what’s different between yours and the Next app and copy the Next app content to your file.

If you are going to use Tailwind and already have a `tailwind.config.js`, like me, you can keep your file and just convert it to Typescript if you wish.

Once you have kept what you need on your config files: `package.json`, `tailwind.config.json`, `tsconfig.json` (if you are already using Typescript), `.gitignore`, you can move the other files from the Next app to the React app (your original app): `next-env.d.ts`, `.eslintrc.json`, `next.config.js`, `postcss.config.js` and `tsconfig.json` (if you don’t already have one) and the `app/` folder. The image below illustrates these changes.


![Image with two screenshots showing the project folders' structure in two moments and an arrow between them pointing from the left to the right. The left screenshot has the following structure: folders are 'docs', 'my-app' (containing folders 'app', 'node_modules' and 'public' and files '.eslintrc.json', 'next-env.d.ts', 'next.config.mjs', 'package-lock.json', 'package.json', 'postcss.config.js', 'README.md', 'tailwind.config.ts' and 'tsconfig.json'), 'node_modules', 'public' and 'src' and files '.gitignore', 'package-lock.json', 'package.json', 'README.md' and 'tailwind.config.js'. The folder 'my-app' has some files highlighted, which are in the root of the project in the right screenshot, also highlighted. These were: 'app' folder and files '.eslintrc.json', 'next-env.d.ts', 'next.config.mjs', 'postcss.config.js' and 'tsconfig.json'. Apart from the mentioned folder and files, the rest of the structure in the right screenshot is identical to the one on the left.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2b667qvb4v2a76spter.png)

Now, you can delete what you won’t need from your react-app. Don’t forget your `README.md`. If you already have one and want to keep it, you can’t delete it from the original files!
I deleted `src/index.js`, `src/reportWebVitals.js` and my test-related files (since I decided not to apply tests now).

Now, my folder structure looks like this:

![A screenshot showing the file structure of the React app. Folders are: 'app', 'docs', 'my-app', 'node_modules', 'public' and 'src' (containing folders named 'components', 'content', 'files', 'img' and files named 'App.css', 'App.js', 'index.css', ). Other files on the root of the project are '.eslintrc.json', '.gitignore', 'next-env.d.ts', 'next.config.js', 'package-lock.json', 'package.json', 'postcss.config.js', 'README.md', 'tailwind.config.js' and 'tsconfig.json'.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r4pxyesqjqjjn6w5ads3.png)


### Adjusting the content

Now the config files are in place it’s time to adjust your content to how Next.js expects to find it. To keep it simple until I'm sure it is working, I started transferring everything from `src/App.js` to `app/page.tsx` - all the logic, states and jsx. 

In this process, you may need to:
- Check if the imports are still working or need any adjust.
- If you are moving from JS to TS, you will need to define some types.
- Check if you need to use client components in your page or any component. Since I am using `useState` and `useEffect` hooks on my page, I had to. To do it, just add `'use client'` on the top of the file. Example:

```
// app/page.tsx
'use client'
import { useState, useEffect } from 'react'

// ... rest of your code
```

Later, I moved my CSS variables from `src/App.css` and `src/index.css` to `app/globals.css` and checked if `app/layout.tsx` and `app/page.tsx` are importing the `app/globals.css`.

Then, I imported my font on `app/layout.tsx` using Next-font and extended it as a Tailwind class, as described in the Next.js official docs - you can find it [here](https://nextjs.org/docs/app/building-your-application/optimizing/fonts#with-tailwind-css).

Then, after the new `package.json` and all files are in place, run in your terminal:

```
npm install
npm run dev
```

`npm install` will update your `package-lock.json` according to your `package.json` and install all the dependencies, hence updating `node_modules` accordingly as well. If it works as expected, `npm run dev` will start the localhost server for you and you can see your project running!

At this point, I came across some compatibility issues I had to deal with (you might have different ones, depending on how your project is). Mine were:

- Some local files I was importing with the `require()` method and it was not working (in `src` and `srcSet` attributes in images and `href` attributes in anchor tag). I found moving the local files to `/public/assets/` and referring their location directly (without mentioning the `/public`) solved the issue. Let me show:

![Screenshot of two lines of code. The first line has a red background and highlighted parts and the second line has a green background with highlighted parts. The code in both lines differs only in the highlighted parts. The code contains an anchor tag (<a></a>) with className, href and target attributes. The first line contains an 'href={require(`../files/${link.url}`)}'. The second line contains 'href={`../assets/pdf/${link.url}`}'.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/euq6y35xwsdm3ufxtu2b.png)

![Screenshot of three lines of code. The first line has a white background and '<img', the second line has a red background and highlighted parts and the second line has a green background with highlighted parts. The code between the second and third lines differs only in the highlighted parts. Both lines have the src attribute. In the second (red) line the src value is '{require(`../img/${project.image.desktop}`)}'. On the third (green) line the src value is '{`../assets/img/${project.image.mobile}`}'.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osi12wj1ftokdpirxg7l.png)

- One `<img />` tag was not working, so I used [next/image](https://nextjs.org/docs/pages/api-reference/components/image).

Once, you’re sure everything is working and that you’ve transferred all the Next app info you need to your original project, you can delete the `my-app` folder you created and the files you will not need anymore (in my case, `src/App.js`, `src/App.css` and `src/index.css`).

### Shipping to production

With all the adjusts done, my project was running smoothly on development. But, when I commited and pushed the modifications to the remote branch, Vercel tried to deploy my branch and I had **errors on deploy**.
What happened is that the project on Vercel has some config related to the framework. It identifies the framework and defines the settings to the deploy (you can read more about it [here](https://vercel.com/docs/deployments/configure-a-build#build-and-development-settings). And, at this point, _my `main` branch was on React and my `nextjs` branch was on Next.js_. I couldn’t figure out a way to have distinct settings for each branch, so my problem was only solved when I integrated the branch into `main`, went to the Project Settings on Vercel, **switched the Framework Preset to Next.js** and tried to redeploy. This is how the Project Settings page looks now:

![Screenshot of a piece of a web page. The title is 'Project Settings'. There is a left menu with options 'General' (selected), 'Domains', 'Integrations', 'Git', 'Functions', 'Data Cache', 'Cron Jobs', 'Environment Variables', 'Deployment Protection', 'Security' and 'Advanced'. The main content has a 'Project Name' section and a 'Build & Development Settings' section.](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ankcdlxn9o0j3zybl86.png)

---

Now, my project is running in both development and production! This is how I got my project running with Next.js without rebuilding it from scratch, I hope it helps you in anyway.

Thanks for reading!