Mar 9, 2023

Host your Motif content at a subpath under your main site

Want yourdomain.com/docs to point to pages on Motif? In this guide, we show how this is possible in a Next.js application.

Next.js rewrites enable you to route an incoming request to your host application to an external destination, such as a Motif project. In this way, you can keep your existing path structure, while using Motif for authoring and serving the content. This is also great for SEO, as all your content is being indexed from a unique origin.

acme.com/docs/*
↓
acme-docs.motif.land/docs/*

Step 1: Create a project on Motif

Head over to Motif and create a project. Your project automatically gets an auto-generated subdomain on motif.land, such as violet-flower-universe.motif.land. In your project settings, you can optionally change that to a more suitable name, such as acme-docs.motif.land, or to a custom domain.

In this example, we will be using the /docs path.

In the Motif project, create a folder named docs in the pages folder, and add a page named index to the docs folder. Make your page public, open the Share menu, and hit Publish site.

Step 2: Set up Next.js rewrites

In order to take full advantage of this, you need to be on Next.js 13.2.4 at least. Earlier versions, such as 13.1.5, will work, except for link prefetching.

In your host Next.js project, skipMiddlewareUrlNormalize and experimental.externalMiddlewareRewritesResolve must be enabled. Your next.config.js should include the following:

module.exports = {
skipMiddlewareUrlNormalize: true,
experimental: {
externalMiddlewareRewritesResolve: true,
},
}

Next, you need to rewrite incoming requests to the Motif app using Next.js middleware. If you haven't already, create a file named middleware.ts in your host project root, and add the following:

import { NextRequest, NextResponse } from 'next/server'
const MOTIF_PROJECT_URL = 'https://acme-docs.motif.land'
const REWRITE_PATH = "/docs"
const dataRe = new RegExp(`^\/_next\/data\/(.*)(${REWRITE_PATH}.*)\$`)
export default async function middleware(req: NextRequest) {
const { pathname } = req.nextUrl
if (pathname === REWRITE_PATH || pathname.startsWith(REWRITE_PATH + '/')) {
// Rewrite /docs/* to external.
return NextResponse.rewrite(MOTIF_PROJECT_URL + pathname)
} else if (pathname.startsWith('/__motif/assets/_next/')) {
// Any assets coming in from external have path
// /__motif/assets/_next/... as per "assetPrefix", so
// rewrite accordingly.
return NextResponse.rewrite(MOTIF_PROJECT_URL + pathname)
} else if (pathname.startsWith('/_next/data/')) {
// Rewrite /_next/data/<buildId>/docs... to external.
const match = pathname.match(dataRe)
const buildId = match?.[1]
const slug = match?.[2]
if (buildId && slug) {
return NextResponse.rewrite(
`${MOTIF_PROJECT_URL}/_next/data/${buildId}${slug}`
)
}
}
return NextResponse.next()
}

Make sure to replace MOTIF_PROJECT_URL and REWRITE_PATH with appropriate values for your setup.


That's it! Whenever someone visits yourdomain.com/docs/some/page/path, they will be served the corresponding page from Motif at acme-docs.motif.land/docs/some/page/path.