Next.js export this:
export type NextPage<P = {}, IP = P> = {
(props: P): JSX.Element | null
defaultProps?: Partial<P>
displayName?: string
getInitialProps?(ctx: NextPageContext): Promise<IP>
}
But also this:
export declare type NextComponentType<C extends BaseContext = NextPageContext, IP = {}, P = {}> = ComponentType<P> & {
getInitialProps?(context: C): IP | Promise<IP>;
};
Is there a reason why NextPage does not implement NextComponentType? I get a painful Typescript error because of that.
@fromi Yes, NextComponentType is used internally (and may not always be used to type the usual pages), meanwhile NextPage defines the page used in your app.
Can you share the error you're getting?
Sure! Actually I created a custom _app page to implement persistent but configurable layout, based on this post: https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/
import App from 'next/app'
class MyApp extends App {
render() {
const {Component} = this.props // Here Component's type is `NextComponentType`
}
}
Doing that I started creating a helper method to check if Component was a Page holding a layout:
export function isLayoutPage(page: NextComponentType): page is LayoutPage {
return (<LayoutPage>page).getLayout !== undefined
}
Then I wanted to reuse "isLayoutPage" in another place, this time on a 'NextPage' typed object. It did not work because NextComponentType and NextPage are not related.
If NextComponentType is meant to be used internally only, then it should not be exposed when _app is overriden I guess :)
Anyway, it is a very minor issue easily fixed, I just wanted to expose the little issue I came by in case there is something you'd like to improve. Next.js is really great!
@fromi I think you're right, using NextComponentType is not a good idea but NextPage should be compatible with it. This is a very edge case but looks like an easy fix 馃憤
@fromi Just checked and the following code works:
const Comp: NextComponentType = () => {
return null
}
const Comp2: NextPage = Comp
This also works:
const Comp: NextPage = () => {
return null
}
const Comp2: NextComponentType = Comp
Can you share a code sample where it fails?
My code changed meanwhile, some I can't find what I did wrong 2 days ago.
I experimented a little bit today and everything looks fine. I probably made a mistake, either by declare a function of 'NextComponentType' where 'C' does not extend 'NextPageContext', or by inverting 'P' and 'IP' (as it is not in the same order in NextComponentType and NextPage).
Everything works great actually, sorry for the bad report!
Ok, I know why I had the issue. Here is an example:
import {NextPage} from 'next'
import App from 'next/app'
class MyApp extends App {
render() {
const {Component, pageProps} = this.props
const page: NextPage = Component // Error: type is not assignable
return <Component {...pageProps}/>
}
}
For know I fail to understand why it fails in this case... Some help would be most welcome!
Ok, I am making progress: with your first example, Typescript automatically knows your NextComponentType is a FunctionComponent and not a ComponentClass.
So, here is a code sample where it fails:
function test(Comp: NextComponentType<NextPageContext, any, {}>) {
const page: NextPage = Comp
}
This way, Typescript does not know whether NextComponentType is a FunctionComponent or a ComponentClass. If it were a ComponentClass, then this function is missing: (props: P): JSX.Element | null
Sure! Actually I created a custom _app page to implement persistent but configurable layout, based on this post: https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/
import App from 'next/app' class MyApp extends App { render() { const {Component} = this.props // Here Component's type is `NextComponentType` } }Doing that I started creating a helper method to check if Component was a Page holding a layout:
export function isLayoutPage(page: NextComponentType): page is LayoutPage { return (<LayoutPage>page).getLayout !== undefined }Then I wanted to reuse "isLayoutPage" in another place, this time on a 'NextPage' typed object. It did not work because NextComponentType and NextPage are not related.
If NextComponentType is meant to be used internally only, then it should not be exposed when _app is overriden I guess :)
Anyway, it is a very minor issue easily fixed, I just wanted to expose the little issue I came by in case there is something you'd like to improve. Next.js is really great!
I was on the same boat and wanted to implement getLayout; do you have a snippet how you did it
I was on the same boat and wanted to implement getLayout; do you have a snippet how you did it
I don't, and anyway it will not be any clearer than Adam Wathan's post, which contains all the snippets you need!
I ended up defining a type like this
export type PageWithLayout = NextPage & {
useLayout: PageLayout;
};
and if my page defines useLayout / getLayout as per this thread i will define them as
(Home as PageWithLayout).useLayout = (page) => ..... //wrap your componenet with the layout you want.
and in my _app.tsx i did this
const MyApp = ({ Component, pageProps, router }: AppProps) => {
const useLayout = (Component as PageWithLayout).useLayout || ((page) => <DefaultLayout>{page}</DefaultLayout>);
return useLayout(<Component {...pageProps} />);
};
Sure! Actually I created a custom _app page to implement persistent but configurable layout, based on this post: https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/
import App from 'next/app' class MyApp extends App { render() { const {Component} = this.props // Here Component's type is `NextComponentType` } }Doing that I started creating a helper method to check if Component was a Page holding a layout:
export function isLayoutPage(page: NextComponentType): page is LayoutPage { return (<LayoutPage>page).getLayout !== undefined }Then I wanted to reuse "isLayoutPage" in another place, this time on a 'NextPage' typed object. It did not work because NextComponentType and NextPage are not related.
If NextComponentType is meant to be used internally only, then it should not be exposed when _app is overriden I guess :)
Anyway, it is a very minor issue easily fixed, I just wanted to expose the little issue I came by in case there is something you'd like to improve. Next.js is really great!
this might be a bit off topic but I was wondering when
implementing persistent and configurable layout, how did you manage 404's , whenever my pages get 404/500 it refreshes, it is rendered in the layout but it is no more persistent; I was wondering if it was my implementation detail that had something wrong or you faced similar experience ?
Most helpful comment
I ended up defining a type like this
and if my page defines useLayout / getLayout as per this thread i will define them as
(Home as PageWithLayout).useLayout = (page) => ..... //wrap your componenet with the layout you want.and in my _app.tsx i did this