An adventure log on discovering the secrets of the getInitialProps function and its mysterious object properties — updated March 2020
Table of contents:
Sometime around 2018, I was working on a web app using Next.js on an older version. At the time, one of the features of Next.js is that it supports initial data population using the getInitialProps
static method, which means that you can populate the page component props
before loading the page (e.g. fetching news feeds).
The latest docs for Next.js is available on their official website. At version 9.1.7 and before, the docs was published on GitHub. Specifically at the "Fetching data and component lifecycle" section, it shows how to use getInitialProps
and what parameters that can be destructured. A snippet from their readme:
getInitialProps
receives a context object with the following properties:
pathname
- path section of URL
query
- query string section of URL parsed as an object
asPath
-String
of the actual path (including the query) shows in the browser
req
- HTTP request object (server only)
res
- HTTP response object (server only)
err
- Error object if any error is encountered during the rendering
Before updating this post, getInitialProps
has one additional property which you can read on their readme at version 8.0.0 and below:
jsonPageRes
- Fetch Response object (client only)
Pretty straightforward, right? Except for one minor problem.
Something’s not right 🔥
On another part of the readme which explains on how to use a custom App
component on _app.js
, it also uses getInitialProps
but with different destructured context parameters. Here's the code snippet from the readme:
From the snippet above, it shows that getInitialProps
doesn't use the documented object properties. And also it seems that I'm not the only one confused about this. Quoting from a discussion on a Spectrum thread,
What damn are and where are coming from the following properties of the context parameter?
So for many weeks, I searched the codebase, issues, and even Spectrum threads related to getInitialProps
. And in this post I will try my best to explain the getInitialProps
debacle.
Inspecting and dumping ✨
In another Spectrum thread that I created, @revskill recommends using util.inspect
to analyze objects. So I made a temporary page (pages/temp.js
) and use this snippet below to dump the getInitialProps
parameter using util.inspect
(note that this is Next.js before version 9):
Checking the console, it returns this:
All properties shown are already documented on the readme, so where's Component
, router
, and ctx
? Because the readme shows that those three properties are used on a custom App, so I made pages/_app.js and dump the parameter on getInitialProps like before (again, note that this is before version 9):
Now the console returns two logs:
As you can see on the snippet above, I destructured two properties: Component
and ctx
. So from my understanding is that the Component
object is the page component that will be loaded (e.g. pages/index.js
), and the ctx
object is the App
context (which explains why it has a router
property). Note the if (Component.getInitialProps)
, it's quite obvious that what it does is checks whether the page component has a getInitialProps function to run.
So what that means is the getInitialProps
parameter (or context) differs from a page component. But this doesn't explain another thing.
Type? What type? 🐴
I’m a sucker for object types, so it really bothers me when statically adding getInitialProps
to an App
or page component obviously doesn't give any hints on my editor. And after inspecting a lot above, at some point I asked myself, "does next
has a @types
package?" And they have! Why did I bother inspecting ony by one?
March 2020 update note: DefinitelyTyped has deprecated the Next.js typings since version 9 already includes its own TypeScript declaration file. You can view the deprecation pull request on GitHub, courtesy of Resi Respati.
After that sudden realization, I added the type package and checked whether it has an object or interface with the name ‘context’ using the IntelliSense extensions on Visual Studio Code. Lo and behold, I found three interfaces ‘context’ related (remember that this is before version 9):
After finding those three, I tried type hinting the getInitialProps
function on both _app.js
and a page component, and the results was fantastic:
👆🏻 App context object properties
👆🏻 Page context object properties
Much better! Now I have found out that it has a @types
package, getting to know more about its type and contents is much easier.
Getting to know more 🚀
In Visual Studio Code, you can jump to the definition of the type by command or control clicking the variable like below:
👆🏻 Preview on control or command clicking the object type
From the GIF above, it opens the declaration files in the node_modules
directory in node_modules/@types/next
. Or you can view the file on the @types/next
repository on GitHub. Here's a snippet from the declaration files for the context object on the App
component (NextAppContext
):
And here’s the context declaration for page components (NextContext
):
That’s much better than inspecting objects one by one. 😅
Hopefully this adventure log isn’t that confusing, since getInitialProps
is already confusing at the start. Thanks for reading, and happy coding! 👋🏻
This was originally posted at Medium on November 26th, 2018 with the same title and contents.
Top comments (1)
I never use getInitialProps or any built in initial props to fetch data, and use custom-hooks instead that work both on Next (SSR) and CRA(CSR). Am I missing something?
I never like the idea of "get this props/data" before your page loaded when doing SSR and have to explicitly call that on every single page. It defeats the purpose of reusable function.