user13329574
user13329574

Reputation:

Two different subdomains in one Next.js routing

I want to build a new platform using Next.js (React.js and React-router). There will be a two spaces. One front-end for users and one for the owner to allow them manage all users. I want to split both areas into two subdomains, like this:

front.domain.com panel.domain.com

React-router does not support sub-domain routing, so I tried to find another approach. I found something like that, but I am not sure that this is what I want. Please advice.

<BrowserRouter>
  <Route path="/" render={props => {
    const [subdomain] = window.location.hostname.split('.');
    if (subdomain === 'panel') return <PanelLayout {...props}/>;
    return <FrontLayout {...props}/>;
  }}/>
</BrowserRouter>

Upvotes: 15

Views: 36534

Answers (6)

Hemal
Hemal

Reputation: 2049

If you host on Vercel now, you can use their wildcard subdomains and have your client-side React code mount the component tree relevant to the subdomain.

Upvotes: 1

Nikasv
Nikasv

Reputation: 1387

As of today you should be able to achieve this with Vercel's Next.js multi-tenancy middleware. To read more about it have a look here https://vercel.com/guides/nextjs-multi-tenant-application

Side note: Middleware is available from Next.js's version 12 and up.

Upvotes: 3

Danny Pule
Danny Pule

Reputation: 1130

You check to see if the pathname from Next's router starts with a pre-defined list of allowed sub-paths

e.g.

const subdomainPaths = {
  request: '/request',
  company: '/company',
};

const isSubdomainRoute = () => {
  const paths = Object.values(subdomainPaths).filter((path) => router.pathname.startsWith(path));
  return !!paths.length;
};

if (window.location.host.includes('subdomain.mysite') && !isSubdomainRoute()) {
  // show 404 or redirect somewhere else
  return <div>404 - not found</div>;
}

Full example:

export const isSSR = typeof window === 'undefined';

export default function App(props: Props) {
  const { Component, pageProps } = props;
  const router = useRouter();

  const subdomainPaths = {
    request: '/request',
    company: '/company',
  };

  const isSubdomainRoute = () => {
    const paths = Object.values(subdomainPaths).filter((path) => router.pathname.startsWith(path));
    return !!paths.length;
  };

  if (!isSSR) {
    if (window.location.host.includes('subdomain.mysite') && !isSubdomainRoute()) {
      // show 404 or redirect somewhere else
      return <div>404 - not found</div>;
    }
  }

  // otherwise fallthrough to the normal Next.js return

  return (
    <>
      <Head>
        <title>My Site</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </Head>
      <AllTheProviders>
        <Component {...pageProps} />
      </AllTheProviders>
    </>
  );
}

Upvotes: 0

Tom S&#246;derlund
Tom S&#246;derlund

Reputation: 4747

Here’s an example of hosting multiple domains on the same Next.js site (while maintaining multiple languages and static site generation/SSG), using Next.js’ i18n system:

https://github.com/tomsoderlund/nextjs-multi-domain-locale

Vercel now supports wildcard domains (*.mysite.com) which is cool!

Upvotes: 10

felixmosh
felixmosh

Reputation: 35503

You can't split 1 Next.js app between sub-domains for several reasons. From an experience, I had a similar requirement (3 areas) I started with one app split into 3 (using sub paths)

  1. Assets (css, js libs) leaks between "areas".
  2. one big app with 3 areas means, each change will require re-deploy all the areas (one big deployable)
  3. Build time, to build 3 areas will be much longer.
  4. Each area may introduce different requirement, such as, UI components for admin area, but custom ui components for the "front" area, Auth, translations and many more

Ended up with 3 separate Next.js apps which managed inside yarn workspaces and get deployed by a specific area.

After I've explained my experience, you can achieve a setup with a reverse-proxy such as nginx to map sub-domain to subpath in your next app.

Let's say you have 3 areas, front, admin, users.

www.domain.com/some-page => should be mapped to localhost:3000/front/some-page. users.domain.com/some-page => should be mapped to localhost:3000/users/some-page. admin.domain.com/some-page => should be mapped to localhost:3000/admin/some-page.

// www.domain.com.conf 

server {
    listen       80;
    server_name  www.domain.com;
    access_log   /var/log/nginx/access.log  main;
    root         html;
 
    location / {
      proxy_pass   http://127.0.0.1:3000/front/; // <-- the last slash is important
    }

  }
// users.domain.com.conf

server {
    listen       80;
    server_name  users.domain.com;
    access_log   /var/log/nginx/access.log  main;
    root         html;
 
    location / {
      proxy_pass   http://127.0.0.1:3000/users/; // <-- the last slash is important
    }

  }

Pay attention

  1. you will need to rewrite static assets as well.

Upvotes: 9

dcangulo
dcangulo

Reputation: 2107

I manage to create subdomains using a custom express server. This is a blank app with no assets, I haven't tried this yet on a real app with assets (CSS, images, etc)

I have the following pages folder structure:

pages/
├── admin/
│   ├── index.js
│   └── sample-page.js
└── member/
    ├── index.js
    └── accounts/
        └── dashboard.js

When you are using next dev which is the default. This will produce the following routes:

But using the custom server.js file and running our dev server using node server.js this will produce the following routes:

The content of our server.js file:

const express = require('express')
const next = require('next')
const vhost = require('vhost')

const port = process.env.PORT || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const mainServer = express()
  const adminServer = express()
  const memberServer = express()

  adminServer.get('/', (req, res) => {
    return app.render(req, res, '/admin', req.query)
  })

  adminServer.get('/*', (req, res) => {
    return app.render(req, res, `/admin${req.path}`, req.query)
  })

  adminServer.all('*', (req, res) => {
    return handle(req, res)
  })

  memberServer.get('/', (req, res) => {
    return app.render(req, res, '/member', req.query)
  })

  memberServer.get('/*', (req, res) => {
    return app.render(req, res, `/member${req.path}`, req.query)
  })

  memberServer.all('*', (req, res) => {
    return handle(req, res)
  })

  mainServer.use(vhost('admin.lvh.me', adminServer))
  mainServer.use(vhost('lvh.me', memberServer))
  mainServer.use(vhost('www.lvh.me', memberServer))
  mainServer.listen(port, (err) => {
    if (err) throw err

    console.log(`> Ready on http://lvh.me:${port}`)
  })
})

See the repo to see this in action.

Repo: https://github.com/dcangulo/nextjs-subdomain-example

Upvotes: 11

Related Questions