vercel / platforms

A full-stack Next.js app with multi-tenancy and custom domain support. Built with Next.js App Router and the Vercel Domains API.
https://app.vercel.pub
5.62k stars 735 forks source link

How to create page with multi-layouts for a cross-platform app ? #286

Closed valentin-harrang closed 10 months ago

valentin-harrang commented 1 year ago

Hello,

I'd like to use Next.js 13 to create a cross-platform application.

Each platform will have its own domain name and each page will have a different layout depending on the domain. So I'm thinking of creating a middleware that will redirect the domain to the app/[domain] folder but I don't think I have any choice but to make a switch case in each layout to know which layout to use.

This example is very good, but there is no layout for each platform: https://vercel.com/guides/nextjs-multi-tenant-application

My questions:

Folder structure:

image

middleware.ts:

import { NextRequest, NextResponse } from "next/server";

export const config = {
  matcher: [
    /*
     * Match all paths except for:
     * 1. /api routes
     * 2. /_next (Next.js internals)
     * 3. /_static (inside /public)
     * 4. all root files inside /public (e.g. /favicon.ico)
     */
    "/((?!api/|_next/|_static/|_vercel|[\\w-]+\\.\\w+).*)",
  ],
};

export default async function middleware(req: NextRequest) {
  const { nextUrl, headers, url } = req;

  const hostname = headers.get("host")!;
  const path = nextUrl.pathname;

  return NextResponse.rewrite(new URL(`/${hostname}${path}`, url));
}

If you have a better idea, don't hesitate to let me know.

Thank you

steven-tey commented 10 months ago

Great question @valentin-harrang! In your case, you'd basically create multiple layout components and have a main layout switcher component that takes in the value from your database and returns the correct layout for the tenant.

You'd make those changes in this component: https://github.com/vercel/platforms/blob/29e20e790eaf17d4d5051c23a69636dce724c174/app/%5Bdomain%5D/layout.tsx#L60-L68

Example:


export default async function SiteLayout({
  params,
  children,
}: {
  params: { domain: string };
  children: ReactNode;
}) {
  const domain = decodeURIComponent(params.domain);
  const data = await getSiteData(domain);

  return (
    <LayoutSwitcher layoutId={data.layoutId}>
        {children}
    </LayoutSwitcher>
  )
}

LMK if that makes sense!