Pass props to page.jsx child from root layout (next.js 13)
Asked Answered
T

6

28

How do you pass props to the the page.jsx of layout? (NEXT 13)

//app/blog/layout.jsx

export default function RootLayout({ children }) {
  return (
    <div>
      <Navbar />
      <Sidebar />
      {/*How do I pass any props from this root layout to this {children} that Im getting from page.jsx*/}
      {children}
    </div>
  );
}

Basically, How do you pass a prop to a function prop (Next. JS 13)?

Terrie answered 25/11, 2022 at 13:2 Comment(1)
Does this answer your question? Retrieve data server side and save in context with Next.jsFreewheeling
V
20

According to the next13 docs you cannot:

It's not possible to pass data between a parent layout and its children. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requests without affecting performance.

Because the layout component refers to a component that defines the overall structure and arrangement of other components within an application or a specific section of the UI. it is not designed to implement state management. its whole purpose is to reduce the time to first render to increase the user experience

But I found a way. In Rootlayout, console.log(props)

export default function RootLayout(props) {
  console.log("props in layout",props)
  return (
        <div>
          {props.children}
        </div>
  );}

this is what you will see

props in layout {
  children: {
    '$$typeof': Symbol(react.element),
    type: {
      '$$typeof': Symbol(react.module.reference),
      filepath: '/home/tesla//node_modules/next/dist/client/components/layout-router.js',
      name: '',
      async: false
    },
    key: null,
    ref: null,
    props: {
      parallelRouterKey: 'children',
      segmentPath: [Array],
      error: undefined,
      errorStyles: undefined,
      loading: undefined,
      loadingStyles: undefined,
      hasLoading: false,
      template: [Object],
      templateStyles: undefined,
      notFound: [Object],
      notFoundStyles: undefined,
      childProp: [Object],
      rootLayoutIncluded: true
    },
    _owner: null,
    _store: {}
  },
  // THIS IS HOW WE PASS PROPS
  params: {}
}

Many properties are not extensible but params. we can dynamically add properties to this object. for example

     props.params.newProp = "testing";

Now visit page.js and

const Page = (props) => {
  console.log("props in page", props);
  return ()}

you will see that props is added to the params object

enter image description here

No matter what I tried, page.tsx had only two props: params and searchParams. searchParams is automatically populated if you have query parameters on url. So, I think params are the only way to pass props from the root layout. you can pass functions too

Varlet answered 26/11, 2022 at 18:46 Comment(13)
Actually, params is the dynamic route params object of the page. So, in a dynamic segment defined as [slug], page.tsx's params prop will contain the following shape: params: { slug: 'some-slug' }.Morton
@Morton correct. I meant to say params are only way to pass props. I am not sure if this is the way to handle it butnext.js has no docs yet and there is alot of issues as of nowVarlet
Oh, I see. As per Next.js docs, it's not possible to pass data between a parent layout and its children. But what you mention in the answer is one interesting finding, didn't know you could extend params object and make the props available for layout's children.Morton
Was looking for the same, but other way around, page to layout. Old _app and Component props could do this. Can't find a way to do it in v13. I guess they tried really hard to make layouts not re-render and preserve state so they removed this functionality. Then they added new fetch and much better caching so idea si that both layout and page fetch the same data which will never be fetched two times during render cycle but cached...Cupel
... Not sure about the boilerplate, but fetch caching might be a good solution. However, what about sharing props that are not in DB and external source and cannot be fetched? Like.... each page changing one css class in header layout (i.e. color of the navigation). Then we have to use context? And what if I want server side component and cannot use context? Tried everything but I think I'll be forced to go back to express/handlebars for my server side rendering of most pages.Cupel
You're supposed to use Context for this, which will work if the variable never changes. Otherwise I'm stuck on this in next app router too.Parse
This indeed only works when set in the root layout. Sadly, no luck when manipulating the parameters in a client component.Pyemia
Cool, but this approach should be avoided since it's not documented. And the general documented information is against this approach (and I have no idea y).Formica
tried but couldnt get it working. any clue? github.com/cmgchess/can-we-pass-props-from-layoutTenatenable
what about if you have navbar, for example, in your Layout and you want it to do things inside of the {children} how would you go about that if you are not allowed to send from Layout to page?Botzow
@Botzow it is not clear what you are asking..Varlet
I have asked it here #78178081Botzow
Ugh I hate that they said "React will automatically dedupe the requests without affecting performance.". However, if u try to add a log into the fetching function, it'll be constantly being calledFriulian
C
5

To pass props from your Layout component(RootLayout for you case) to page.jsx.

//app/blog/layout.jsx

export interface items {
  foo: string;
  bar: string;
}

export default function RootLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: {
    foo: string;
    items: items;
    age: number;

  };
}) {

  params.foo = "bar"; //string example

  params.items = { foo: "bar", bar: "foo" }; //object example

  params.age= 1; //number example

  return (
    <html lang="en" className="">
      <body>{children}</body>
    </html>
  );
}

//app/blog/page.jsx

export default function Dashboard({
  params,
}: {
  params: { foo: string; items: items; age: number };
}) {
  console.log(params.foo); //bar
  console.log(params.items); //{foo: 'bar', bar: 'foo'}
  console.log(params.age); //1
  return (
    <div></div>
  );
}
Corwun answered 17/4, 2023 at 18:45 Comment(6)
I tried this, it doesn't workHalide
Are you able to show the code?Corwun
not anymore, but I think it didn't work because I tried to pass an object. Perhaps if I would stringify it, it would work. Maybe it's not a good idea, because it can contain a lot of data.Halide
Hi, are you able to share some code? I tried with an object right now and its still working!Corwun
sorry I didn't get back to you, but I am not working on that project anymore.Halide
This can only ever be used to pass strings, also you can not use this to pass components, since components are not serializable. this approach is not documented as well.Formica
F
3

Accepted answer is a hack and should be avoided. You don't pass value from layout to page, instead you get the value in the page component directly.

You can optionally cache the value in the layout, using React "cache" function. See this question about server context, the answers explains how you can then pass a value from the page to its own children without relying on props drilling.

Freewheeling answered 12/2 at 17:31 Comment(2)
what about if you have navbar, for example, in your Layout and you want it to do things inside of the {children} how would you go about that:?Botzow
You can create a layout component e.g. component/Layout.tsx with all reusable components like navbar as children and use it as the layout in your pages.Wirephoto
M
1

You can just utilize React Context:

// layout.tsx

import React from 'react';

export const PageContext = React.createContext(null);

export default function SomeLayout({children}) {
   const [search, setSearch] = React.useState('foo');
   return <PageContext.Provider value={search}>{children}</PageContext.Provider>
}
// page.tsx

import React from 'react';
import PageContext from '../layout.tsx';

export default function SomePage() {
   const search = React.useContext(PageContext);
   console.log(search);
   return null;
}
Massarelli answered 19/8, 2023 at 9:24 Comment(3)
I think no other way without using context. Thanks!Tweet
so simple, yet so effective!Lagerkvist
It's not possible to use useContext in any server component.Duchamp
P
1

you can handle it like this

const layout = ({children}: { children: React.ReactNode }) => {
const tabs = [
    {
        prefix: 'single',
        link: router.business.simcards.simcardsList,
        icon: (props) => {return( <SingleSimcardSVG {...props}/> )},
        isActive: false
    },
   
]
const childrenWithTabs = React.Children.toArray(children).map((child) => {
    if (React.isValidElement(child) && typeof child.type === 'function') {
        return React.cloneElement(child, { tabs } as any);
    }
    return child;
});
return (
    <>
        {childrenWithTabs}
    </>
)}

export default layout

and in your child can access to props.tabs

Pasia answered 29/10, 2023 at 10:23 Comment(1)
Anyway it didn't work, it gives undefinedFormica
U
-2

In case if you want to send this props of children to Navbar component

<Navbar children = {children} />
Ursel answered 25/11, 2022 at 13:12 Comment(2)
No, I have a variable in root layout that I want to share with the to the {children} that was passed to the root layout. Not the navbarTerrie
Are you able to show the code where you have that variableUrsel

© 2022 - 2024 — McMap. All rights reserved.