Using getInitialProps in Next.js with TypeScript
Asked Answered
S

6

48

From the documentation, Next.js 5.0 announcement and various articles around on the internet it seems like Next.js supports TypeScript well and many people are using it.

But these threads suggest that getInitialProps, which is vital to Next.js apps, doesn't work:

How can I fix it? In both class and functional components, when I do ComponentName.getInitialProps = async function() {...} I get the following error:

[ts] Property 'getInitialProps' does not exist on type '({ data }: { data: any; }) => Element'.
Suhail answered 19/4, 2018 at 19:58 Comment(0)
C
81

Edit 2021/04/23: My answer below is also out of date

Please see the Next.js docs for the current recommendation for typing data fetching methods using new types and named exports.

tl;dr:

import { GetStaticProps, GetStaticPaths, GetServerSideProps } from 'next'

export const getStaticProps: GetStaticProps = async (context) => {
  // ...
}

export const getStaticPaths: GetStaticPaths = async () => {
  // ...
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  // ...
}


Out of date (but functional) answer:

The above answers are out of date since Next.js now officially supports TypeScript (announcement here)

Part of this release is better TypeScript types, with much of Next.js being written in TypeScript itself. This means that the @types/next package will be deprecated in favour of the official Next.js typings.

Instead, you should import the NextPage type and assign that to your component. You can also type getInitialProps using the NextPageContext type.

import { NextPage, NextPageContext } from 'next';

const MyComponent: NextPage<MyPropsInterface> = props => (
  // ...
)

interface Context extends NextPageContext {
  // any modifications to the default context, e.g. query types
}

MyComponent.getInitialProps = async (ctx: Context) => {
  // ...
  return props
}

Catamaran answered 10/8, 2019 at 9:56 Comment(7)
How would that look like for components that are not pages?Ruche
@Ruche Non-page components can't use getInitialProps. Therefore, you'd just type it like a normal react component (some interface for props, inferred return type is my favourite. You can also use React.FunctionComponent)Catamaran
thanks. "some interface for props, inferred return type is my favourite" - can you elaborate a bit on that? not sure how that could look like.Ruche
@Ruche interface Props { propName: string; }; const MyComponent = (props: Props) => ( /* ... */ ) Catamaran
Does any know how to fix @typescript-eslint/unbound-method? It highlights MyComponent in MyComponent.getInitialProps = ...Wimble
@DanielCheung, have a look at my edit above. You should be able to use named exports as opposed to static methods now which will work around that eslint warning.Catamaran
@JamesMulholland Can you please edit your example with code how to apply modifications to the NextPageContext query type.Mountainous
D
25

EDIT: this answer is out of date since the release of Next 9. See answer above.

The classical way to solve the problem is to declare getInitialProps as a static member:

class MyComponent extends React.Component<{...}, {}> {

  static async getInitialProps(ctx: any) {
    return {...}
  }

  render() {...}

}

When working with stateless components, you can declare a simple extension of React.SFC:

interface StatelessPage<P = {}> extends React.SFC<P> {
  getInitialProps?: (ctx: any) => Promise<P>
}

const MyComponent: StatelessPage<{...}> = (...) => ...

MyComponent.getInitialProps = async (ctx) => {...}
Dykes answered 18/5, 2018 at 15:36 Comment(1)
Although usable, this answer is out of date since the release of Next 9. See my answer below on how to use official Next.js typingsCatamaran
W
12

Types for Next.js are maintained in the DefinitelyTyped project which has a new version 7.0.6.

In order to use the new types, make sure you are importing them in your project:

npm install --save-dev @types/[email protected]

Here is how you type getInitialProps for a stateless functional component:

import { NextFunctionComponent, NextContext } from 'next'

// Define what an individual item looks like
interface IDataObject {
  id: number,
  name: string
}

// Define the props that getInitialProps will inject into the component
interface IListComponentProps {
  items: IDataObject[]
}

const List: NextFunctionComponent<IListComponentProps> = ({ items }) => (
  <ul>
    {items.map((item) => (
      <li key={item.id}>
        {item.id} -- {item.name}
      </li>
    ))}
  </ul>
)

List.getInitialProps = async ({ pathname }: NextContext) => {
  const dataArray: IDataObject[] =
    [{ id: 101, name: 'larry' }, { id: 102, name: 'sam' }, { id: 103, name: 'jill' }, { id: 104, name: pathname }]

  return { items: dataArray }
}

export default List

Here is how you type getInitialProps for a class:

import React from 'react'
import { NextContext } from 'next'

// Define what an individual item looks like
interface IDataObject {
  id: number,
  name: string
}

// Define the props that getInitialProps will inject into the component
interface IListClassProps {
  items: IDataObject[]
}

class List extends React.Component<IListClassProps> {
  static async getInitialProps({ pathname }: NextContext) {
    const dataArray: IDataObject[] =
      [{ id: 101, name: 'larry' }, { id: 102, name: 'sam' }, { id: 103, name: 'jill' }, { id: 104, name: pathname }]

    return { items: dataArray }
  }

  render() {
    return (
      <ul>
        {this.props.items.map((item) => (
          <li key={item.id}>
            {item.id} -- {item.name}
          </li>
        ))}
      </ul>
    )
  }
}

export default List

If you review the tests in DefinitelyTyped, you can get a lot of insights on how to use other variations of the typings for Next.

Wier answered 22/1, 2019 at 12:41 Comment(1)
Although good, this answer is out of date since the release of Next 9. See my answer belowCatamaran
D
0

Following the documentation

import React from 'react'
import { NextPageContext } from 'next'

interface Props {
  userAgent?: string;
}

export default class Page extends React.Component<Props> {
  static async getInitialProps({ req }: NextPageContext) {
    const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
    return { userAgent }
  }

  render() {
    const { userAgent } = this.props
    return <main>Your user agent: {userAgent}</main>
  }
}
Deliver answered 21/2, 2020 at 16:35 Comment(0)
V
0

Easy solution is declare type:

declare global {
   namespace React {
      interface FunctionComponent<P = {}> {
      getInitialProps(): void;
}}}
Vilipend answered 27/5, 2021 at 14:52 Comment(0)
B
0

If you're using TypeScript, you can use the NextPage type for function components

Nextjs DOC

Bunny answered 26/8, 2022 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.