Multiple Layouts Same Route
Answered
necm1 posted this in #help-forum
necm1OP
Hi!
I'm trying to create a multi-tenant application where users have the possibility to choose between multiple layouts & landings inside a nx monorepo.
Actually my question is: Can I use multiple layouts for the same route, but choose / select them depending on the configuration?
basically, it could look like this
Currently I've a provider, which imports all layouts and in my layout I simply do a request to my API, which returns the selected template (for example template1) and in my BaseLayout-Component file I just try to fetch the information and return the selected Layout.
I guess this could leak to a performance issue, due to importing all layouts in my provider and try to get the correct one, based on the returned string from my API.
It's working, but dunno if it could be a issue in the future by adding more templates.
I'm trying to create a multi-tenant application where users have the possibility to choose between multiple layouts & landings inside a nx monorepo.
Actually my question is: Can I use multiple layouts for the same route, but choose / select them depending on the configuration?
basically, it could look like this
/app
layout.tsx <- Root
(template1)
layout.tsx <- Selectable layout for domain
/overview
page.tsx
(template2) <- Selectable layout for domain
layout.tsx
/overview
page.tsxCurrently I've a provider, which imports all layouts and in my layout I simply do a request to my API, which returns the selected template (for example template1) and in my BaseLayout-Component file I just try to fetch the information and return the selected Layout.
I guess this could leak to a performance issue, due to importing all layouts in my provider and try to get the correct one, based on the returned string from my API.
It's working, but dunno if it could be a issue in the future by adding more templates.
Answered by Ray
maybe you could try this with parallel routes?
export default async function Layout({
template1,
template2,
}: {
template1: React.ReactNode;
template2: React.ReactNode;
}) {
const template = await fetchTemplate();
switch (template) {
case "a":
return template1;
case "b":
return template2;
default:
<div>default layout</div>;
}
}61 Replies
@necm1 Hi!
I'm trying to create a multi-tenant application where users have the possibility to choose between multiple layouts & landings inside a nx monorepo.
Actually my question is: Can I use multiple layouts for the same route, but choose / select them depending on the configuration?
basically, it could look like this
/app
layout.tsx <- Root
(template1)
layout.tsx <- Selectable layout for domain
/overview
page.tsx
(template2) <- Selectable layout for domain
layout.tsx
/overview
page.tsx
Currently I've a provider, which imports all layouts and in my layout I simply do a request to my API, which returns the selected template (for example template1) and in my BaseLayout-Component file I just try to fetch the information and return the selected Layout.
I guess this could leak to a performance issue, due to importing all layouts in my provider and try to get the correct one, based on the returned string from my API.
It's working, but dunno if it could be a issue in the future by adding more templates.
maybe you could try this with parallel routes?
export default async function Layout({
template1,
template2,
}: {
template1: React.ReactNode;
template2: React.ReactNode;
}) {
const template = await fetchTemplate();
switch (template) {
case "a":
return template1;
case "b":
return template2;
default:
<div>default layout</div>;
}
}Answer
@Ray maybe you could try this with parallel routes?
tsx
export default async function Layout({
template1,
template2,
}: {
template1: React.ReactNode;
template2: React.ReactNode;
}) {
const template = await fetchTemplate();
switch (template) {
case "a":
return template1;
case "b":
return template2;
default:
<div>default layout</div>;
}
}
necm1OP
so every template folder should be like this, right?
/app
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
@template2
layout.tsx
/overview
page.tsxjust a little bit confused, because the docs says:
render a @dashboard or @login route depending on the authentication state.so it should basically be an route and not a layout
🤔
@necm1 so it should basically be an route and not a layout
oh so just the
layout.tsx is different?actually everything would be kinda different
from the UI / UX perspective
but they contain the same functions
maybe better to do it in a component
@Ray maybe better to do it in a component
necm1OP
thats what I've now
bascially every page or even the main layout have a BaseLayout / BasePage
which gets the template name passed into it from the TemplateProvider (which received the data from the RootLayout by fetching the domain information and passes it into the Provider Context)
and I store the Layout including the Pages into an object like this:
const templates = [
"template-1": {
Layout: TemplateOneLayout,
OverviewPage: TemplateOneOverviewPage
},
.....
]but dunno if this could affect my perfomance over the while
/app
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
/overview
page.tsx
@template2
layout.tsx
/overview
page.tsx
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
/overview
page.tsx
@template2
layout.tsx
/overview
page.tsx
@Ray /app
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
/overview
page.tsx
@template2
layout.tsx
/overview
page.tsx
necm1OP
would this approach load the included
layout.tsx and let me call /overview?what do you mean?
necm1OP
So, the docs say that the parallel routing in this particular case would use the route (alias
page.tsx) based on a condition like isAuthenticated@Ray /app
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
/overview
page.tsx
@template2
layout.tsx
/overview
page.tsx
necm1OP
but I want to use the
layout.tsx provided in this exampleyou could have
layout.tsx inside the parallel route@Ray /app
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
/overview
page.tsx
@template2
layout.tsx
/overview
page.tsx
like this structure, you could also create other route inside
@Ray maybe you could try this with parallel routes?
tsx
export default async function Layout({
template1,
template2,
}: {
template1: React.ReactNode;
template2: React.ReactNode;
}) {
const template = await fetchTemplate();
switch (template) {
case "a":
return template1;
case "b":
return template2;
default:
<div>default layout</div>;
}
}
necm1OP
so I could create the same route, but having a different "parent"-layout, which is pre-selected trough the RootLayout like here
yes
I would suggest you init a new project and play with it
necm1OP
and based on the layout params when I return
template1 (which points to @template1) it would prolly use the page inside @template1/overview/page.tsx, right?@Ray I would suggest you init a new project and play with it
necm1OP
definitely going to do that, just wanted to make things clear, to understand it :D
@necm1 and based on the layout params when I return `template1` (which points to `@template1`) it would prolly use the page inside `@template1/overview/page.tsx`, right?
yes if the current url is /overview then
@template1/overview/page.tsx will renderyou could also have different url structure for different parallel route
eg, only @template2 have /only-template2 this url
@Ray yes if the current url is /overview then `@template1/overview/page.tsx` will render
necm1OP
but I could go the same way like this:
/app
layout.tsx <- Root including your code
@template1
layout.tsx <- Selectable layout for domain
/overview
page.tsx
@template2
layout.tsx
/overview
page.tsx
/overview <- is in app folder
layout.tsx <- main overview route, which the layout have some SSR fetching and render the page, based on the given templateright?
cuz every route have the same api fetchings, but only the layout / components gonna change
but will use the same fetched data
if so, you have to create the parallel route in a route group like this
/app
layout.tsx <- Root including your code
(template)
layout.tsx <- parallet layout
@template1
layout.tsx <- Selectable layout for domain
@template2
layout.tsx\
/overview
/app
layout.tsx <- Root including your code
(template)
layout.tsx <- parallet layout
@template1
layout.tsx <- Selectable layout for domain
@template2
layout.tsx\
/overview
@necm1 cuz every route have the same api fetchings, but only the layout / components gonna change
oh well, then better do it with component
or you try the parallel route in a new project first since I still not 100% understand how you gonna make the app

necm1OP
alright, thank you. I'll test it now and give some feedback afterwards ðŸ˜
yeah ask here if you are facing issue
necm1OP
thanks for your time. I'll do that! :D
@Ray maybe you could try this with parallel routes?
tsx
export default async function Layout({
template1,
template2,
}: {
template1: React.ReactNode;
template2: React.ReactNode;
}) {
const template = await fetchTemplate();
switch (template) {
case "a":
return template1;
case "b":
return template2;
default:
<div>default layout</div>;
}
}
necm1OP
tried this one now, but the given template params in the layout seems to be undefined
export default async function RootLayout({
template1,
}: {
template1: React.ReactNode;
}) {
const template = await getDomain(); // returns template-1
const templates: { [key: string]: React.ReactNode } = {
'template-1': template1,
};
console.log('template', template, 'react node', templates[template]); // output: template template-1, react node: undefined
return templates[template] || <h2>not found</h2>;
}@necm1 tried this one now, but the given template params in the layout seems to be undefined
sometime, It might need to remove
.next and restart the servernecm1OP
oh, gonna try that one
@Ray sometime, It might need to remove `.next` and restart the server
necm1OP
worked perfectly. Thank you very much
Elm sawfly
@necm1 I have the same need for a project I am working on. Any chance you could share your solution on a gist or something?
@Elm sawfly <@315806345376825344> I have the same need for a project I am working on. Any chance you could share your solution on a gist or something?
necm1OP
sure. just give me some time I’ve a meeting now
@necm1 sure. just give me some time I’ve a meeting now
Elm sawfly
thanks man. really looking forward to this
@Elm sawfly thanks man. really looking forward to this
necm1OP
sorry, currently at the gym right now but after that I'll post the solution here
Elm sawfly
hey mate - any chance you can post that sometime tonight? if you aren't comfortable with sharing it, I'll understand and move forward without. but would really appreciate the assist otherwise 🙂
@Elm sawfly hey mate - any chance you can post that sometime tonight? if you aren't comfortable with sharing it, I'll understand and move forward without. but would really appreciate the assist otherwise 🙂
necm1OP
could you give me an example what you try to achieve and what you already tried?
thats basically my structure
(panel)/layout.tsx:
export async function getDomain() {
const protocol = headers().get('x-forwarded-proto');
const host = headers().get('host');
const url = `${protocol}://${
host?.includes(':') ? host.split(':')[0] : host
}`;
const res = await POST('domain-manager/info', {
body: { url },
});
if (res.status && res.status === 403) {
return [];
}
return (await res.json()).data.template;
}
export default async function PanelLayout({
template1,
}: {
template1: React.ReactNode;
}) {
const template = await getDomain();
const templates: { [key: string]: React.ReactNode } = {
'template-1': template1,
};
return <Fragment>{templates[template] || <h2>not found panel</h2>}</Fragment>;
}(panel)/@template1/layout.tsx
export default async function TemplateOneLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html className={inter.className}>
<body>
<Layout>{children}</Layout>
<Toaster
toastOptions={{
classNames: {
toast: 'group-[.toaster]:border-none',
},
}}
/>
</body>
</html>
);
}and inside the template1/layout.tsx I simply use the components for template1 like the
Elm sawfly
this worked out for me - thanks so much!