2022-03-27
|~2 min read
|382 words
NextJS has documentation for layouts. It even has a section for Typescript.
Unfortunately, when I tried to follow it, I ran into a whole host of issues.
Fortunately, I found enough clues online to solve it (and with help from the NextJS and Typescript discords respectively).
How does it work?
Well, let’s start with the _app.ts
file:
// pages/_app.ts
import React from "react"
import { getLayout as getDefaultLayout } from "../components/SiteLayout"
import { AppPropsWithLayout } from "../types"
function MyApp({ Component, pageProps }: AppPropsWithLayout) {
const getLayout =
Component.getLayout ??
((page: AppPropsWithLayout) => getDefaultLayout(page))
return getLayout(<Component {...pageProps} />)
}
export default MyApp
The key point is that at this point, we’re still in line with the Next.JS documentation for Typescript, but that’s only because of the fallback with the getDefaultLayout
.
The getDefaultLayout
is pretty basic right now:
//components/SiteLayout.tsx
import Link from "next/link"
import React from "react"
export const SiteLayout = ({ children }: React.PropsWithChildren<unknown>) => (
// apply any layout you want by default
<div>{children}</div>
)
export const getLayout = (page: any) => <SiteLayout>{page}</SiteLayout>
export default SiteLayout
The point is that when you apply a getLayout
to a exported component as a function, you need to use the same NextPageWithLayout
:
For example, our Index.tsx
:
//pages/Index.tsx
import Link from "next/link"
import { getLayout } from "../components/SiteLayout"
import { NextPageWithLayout } from "../types"
const Index: NextPageWithLayout<unknown> = () => (
<div className="mt-8 max-w-xl mx-auto px-8">
<h1 className="text-center">
<span className="block text-xl text-gray-600 leading-tight">
Welcome to this
</span>
<span className="block text-5xl font-bold leading-none">
Awesome Website
</span>
</h1>
<div className="mt-12 text-center">
<Link href="/account-settings/basic-information">
<a className="inline-block bg-gray-900 hover:bg-gray-800 text-white font-medium rounded-lg px-6 py-4 leading-tight">
View account settings
</a>
</Link>
</div>
</div>
)
Index.getLayout = getLayout
export default Index
Where does that type come from? Well, we defined our own wrapper in types.d.ts
(I’d love to know if this is the right place to define these sorts of types):
//types.d.ts
import { NextPage } from "next"
import { AppProps } from "next/app"
import { ReactElement, ReactNode } from "react"
export type NextPageWithLayout<T> = NextPage<T> & {
getLayout?: (page: ReactElement) => ReactNode
}
export type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
I pulled together a simple repo (based on Adam Wathan’s blog post) to demonstrate how to adapt it to Typescript.
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!