Written by Robert Koch
To start with I followed the same steps in the upgrade guide, I installed the latest version of next and react via npm.
npm i next@latest react@latest react-dom@latest
This went fine and without a hitch.
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.
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.npx @next/codemod next-image-to-legacy-image .
Image
functionality you can run the
following codemod to update all Image
components to the new one.npx @next/codemod next-image-experimental .
Link
components to use the new functionality where
they don't need an a
tag inside them.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.
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
.1<div className="min-h-screen flex flex-col overflow-hidden">2 <Topbar />3 <div className="flex-grow">{children}</div>4 <Footer5 title={'Kochie Engineering'}6 links={7 // ...8 }9 />10</div>
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
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 thepage.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.globalTypeError: Converting circular structure to JSON--> starting at object with constructor 'global'--- property 'global' closes the circle
use client
directive at the
beginning of the page.content
property in your tailwind.config.js
file to the following.module.exports = {content: ['./src/app/**/*.{js,ts,jsx,tsx}'],}
app
directory for any css classes.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.1'use client'2import { load, trackPageview } from 'fathom-client'3import { useEffect } from 'react'4import {5 usePathname,6 useSearchParams7} from 'next/navigation'89export default function Fathom() {10 const pathname = usePathname()11 const searchParams = useSearchParams()1213 useEffect(() => {14 load('XXXXXXXX', {15 includedDomains: ['blog.kochie.io'],16 spa: 'auto',17 })1819 trackPageview()20 }, [pathname, searchParams])2122 return null23}
This client component is loaded on the initial page load and the hook will fire when the path changes or parameters change.
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.next-seo
package
provides support for the app directory
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.1export default async function Head({ params }: { params: { authorId: string }}) {2 return (3 <>4 <NextSeo {...NEXT_SEO_DEFAULT} useAppDir={true} />5 </>6 )7}
next-mdx-renderer
which seemed to break on the
latest version. But there was a trick I saw shadcn
use in their latest app 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.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.1'use client'23import { MDXRemote, type MDXRemoteProps } from 'next-mdx-remote'4import * as components from './components'5import React from 'react'67export const MDXContent = (props: MDXRemoteProps) => {8 return <MDXRemote {...props} components={components} />9}
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.
next-pwa
that provides the functionality. At the time of writing
next-pwa
does not support the new
app directory, but there has been a
pull request 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.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.
In closing, the new layouts system will soon become the defacto 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.