# Migrating to Next.js 13

> Sometimes migrating to a new version isn't worth it, I document all the issues I ran into when migrating to Next.js 13.

- URL: https://blog.kochie.io/articles/06-migrating-to-next13
- Author: Robert Koch
- Published: 2022-11-29
- Updated: 2022-12-01
- Tags: technology, business, 100DTO
- Project: https://blog.kochie.io/projects/personal-site

---

Over the last couple of weeks I've been updating this blog to use the new
[layouts system in Next.js 13](https://beta.nextjs.org/docs/routing/fundamentals).
With the publication of this post I am very excited to announce that this site
is running completely on next.js 13 and server components via Vercel edge
functions.

It's been interesting to refactor and while the official
[upgrade guide](https://beta.nextjs.org/docs/upgrade-guide) covers a lot of the
basics, there are still some gotchas that can trip you up. I'm going to document
all the issues I had migrating to Next.js 13 and how I solved them.

## Installing the latest version of Next.js

To start with I followed the same steps in the upgrade guide, I installed the
latest version of next and react via npm.

```bash
npm i next@latest react@latest react-dom@latest
```

This went fine and without a hitch.

## Running Codemods for the latest functionality

After this, the next step is to upgrade to the latest features in the built-in
components like `Link` and `Image`. Next has a tool called `codemod` that will
scan your entire project looking for updates that need to be applied to use the
new features.

> You do need to commit any changes before running a codemod command otherwise
> it will not work.

The first step is to convert all `next/image` imports into `next/legacy/image`
imports as version 13 of next.js has a new image component that breaks some
functionality with the old component.

```bash
npx @next/codemod next-image-to-legacy-image .
```

Additionally, if you want the new `Image` functionality you can run the
following codemod to update all `Image` components to the new one.

```bash
npx @next/codemod next-image-experimental .
```

Now this _should_ not break any images you have on your page, but there's no
guarantee depending on how you set up the page.

Finally you can update any `Link` components to use the new functionality where
they don't need an `a` tag inside them.

```bash
npx @next/codemod new-link .
```

Now I found that most of these codemods worked fine, but there were some edge
cases where the updates didn't do what I expected so make sure you manually
check to see what the difference these codemods perform.

## Layouts

Of course the big new feature of the new version of next is the `app` directory
which uses a completely new layout system. Now it's possible to build a page
using a tree structure to render components asynchronously from each other. Say
for example you have a common layout that all your pages use (like myself), you
can now specify this layout in the root of the `app` directory in `layout.tsx`.

```tsx lineNumbers
<div className="min-h-screen flex flex-col overflow-hidden">
  <Topbar />
  <div className="flex-grow">{children}</div>
  <Footer
    title={'Kochie Engineering'}
    links={
      // ...
    }
  />
</div>
```

Now this structure will apply to all the pages nested within the directory. In
terms of migrating to this structure the best way to do it is to move one page
over at a time. Beware however as it's not as easy as copying the content over,
for example, the `Head` component no longer works and you now require a
`head.tsx` for every `page.tsx` file you have if you want to manipulate the head
section of the page.

```
src/pages
├── _app.tsx
├── _document.tsx
├── _error.js
├── articles
│   └── [articleId].tsx
├── authors
│   ├── [authorId].tsx
│   └── index.tsx
├── index.tsx
└── tags
    ├── [tagId].tsx
    └── index.tsx
```

This changed my file layout from the `pages` directory (seen above) to this new
`app` directory below.

```
src/app
├── articles
│   └── [articleId]
│       ├── head.tsx
│       └── page.tsx
├── authors
│   ├── [authorId]
│   │   ├── head.tsx
│   │   └── page.tsx
│   ├── error.tsx
│   ├── head.tsx
│   └── page.tsx
├── head.tsx
├── layout.tsx
├── page.tsx
└── tags
    ├── [tagId]
    │   ├── head.tsx
    │   └── page.tsx
    ├── head.tsx
    └── page.tsx
```

> At the time of writing you can't change the title when navigating from another
> page without editing it in the `page.tsx` file. This is getting fixed.

One issue I had early on was many pages crashing the server due to incompatible
server-side components. The error was similar to the one below.

```
Warning: Only plain objects can be passed to Client Components from Server Components. global objects are not supported.
  <>{</>}{</>}{</>}</>
         ^^^^^
Warning: Only plain objects can be passed to Client Components from Server Components. global objects are not supported.global
TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'global'
    --- property 'global' closes the circle

```

To fix this unfortunately you need to go through each component and use a
process of elimination to figure out what component is causing the error. Once
you find the right component you can just use the `use client` directive at the
beginning of the page.

## Font Awesome

I had [some issues](https://github.com/FortAwesome/Font-Awesome/issues/19193)
with Font Awesome's react components not working properly with server-side
rendering. These issues seem to be fixed now but look out for issues related to
loading the library still.

## Tailwindcss

Tailwind is fortunately pretty easy to update, all you need to do is change the
`content` property in your `tailwind.config.js` file to the following.

```js
module.exports = {
  content: ['./src/app/**/*.{js,ts,jsx,tsx}'],
}
```

This makes sure that tailwind scans the new `app` directory for any css classes.

## Fathom Analytics

I'm using [Fathom Analytics](https://usefathom.com/) to track pageviews on this
site and I've had to make a few changes for it to work. Originally the way to
use it was
[well documented](https://vercel.com/guides/deploying-nextjs-using-fathom-analytics-with-vercel)
but now with the new layouts system the `useRouter` hook that this solution
depends on doesn't work anymore. The solution I've found is to create a client
component in the root layout that updates when a new layout path has changed.

```tsx lineNumbers
'use client'
import { load, trackPageview } from 'fathom-client'
import { useEffect } from 'react'
import { usePathname, useSearchParams } from 'next/navigation'

export default function Fathom() {
  const pathname = usePathname()
  const searchParams = useSearchParams()

  useEffect(() => {
    load('XXXXXXXX', {
      includedDomains: ['blog.kochie.io'],
      spa: 'auto',
    })

    trackPageview()
  }, [pathname, searchParams])

  return null
}
```

This client component is loaded on the initial page load and the hook will fire
when the path changes or parameters change.

## OpenGraph and SEO

OpenGraph is a bit interesting, I'm using the `next-seo` package to generate
most of the SEO tags for my page and a custom puppeteer script to create the
OpenGraph images. Recently and unrelated to Next13 chromium has not been
installing properly in Vercel build scripts so I've had to migrate over to
Vercel's library to create the images. The new `@vercel/og` library uses edge
functions to generate the images on the fly, in the beginning I had some issues
getting it to work properly with the styling I wanted but I've been able to
configure it to my liking.

As for SEO meta tags, the updated `next-seo` package
[provides support for the app directory](https://github.com/garmeeh/next-seo/blob/master/APP_DIRECTORY.md)
supports a similar pattern for defining the needed tags. For each `page.tsx` you
can export a `head.tsx` and have the head render for each page.

```tsx lineNumbers
export default async function Head({
  params,
}: {
  params: { authorId: string }
}) {
  return (
    <>
      <NextSeo {...NEXT_SEO_DEFAULT} useAppDir={true} />
    </>
  )
}
```

## MDX Rendering

Initially I was stuck on how I would implement mdx rendering with the new
system. Previously I was using `next-mdx-renderer` which seemed to break on the
latest version. But there was a trick I saw [shadcn](https://twitter.com/shadcn)
use in their latest app [taxonomy](https://github.com/shadcn/taxonomy) that
helped me fix the issues I was facing. See with the new split of server and
client react components you have to decide what parts of the interface should be
built in the clients browser.

By creating a wrapper around the `MDXRemote` component I was able to make the
mdx render on the client side and not the server. This is a pattern I've used
throughout the project and it's quite useful.

```tsx lineNumbers
'use client'

import { MDXRemote, type MDXRemoteProps } from 'next-mdx-remote'
import * as components from './components'
import React from 'react'

export const MDXContent = (props: MDXRemoteProps) => {
  return <MDXRemote {...props} components={components} />
}
```

I don't exactly like this solution, mainly because it would be more efficient
for the mdx to be compiled and rendered on the server side and only required
client components sent down the wire, but for now it seems to work.

## Progressive Web Apps (PWAs)

Next.js does not support PWAs out of the box, but there is a package called
`next-pwa` that provides the functionality. At the time of writing
[`next-pwa`](https://www.npmjs.com/package/next-pwa) does not support the new
app directory, but there has been a
[pull request](https://github.com/shadowwalker/next-pwa/pull/427) for a few
weeks now that does add the functionality. Hopefully once it's merged in there
will be feature parity between the `pages` and `app` directories.

## Should you refactor to use server components?

For the amount of work required and the number of bugs and compatibility issues,
I would say no. The reality is that the new layouts system is still in beta and
even Vercel does not suggest running it in production.

If you're working on a new site I think it's worth using instead of the old
pages setup but refactoring old sites is not needed, I haven't seen a
significant speed increase in my static site.

The real advantage of using layouts is the ability to interact with the
[react suspense](https://uk.reactjs.org/docs/concurrent-mode-suspense.html)
pattern and render different parts of a page while other parts are still
loading, allowing for a more dynamic and interactive experience.

In closing, the new layouts system will soon become the de facto way to build
websites with Next.js, but the effort required at the moment to refactor a
webpage is still too high to justify the benefits.
