How to Manage Multiple Domains with a Headless CMS
Anton Meleshkin
Posted on August 26, 2024
Hello, fellow developers! Today, we’re diving into a common challenge: "How can I manage 2+ domains under one CMS space?" Luckily, with modern technologies, this is easier than you might think.
For this guide, we'll use Storyblok as our headless CMS (hCMS) of choice due to its intuitive design and flexible features. However, if you’re using a different hCMS, you can still apply the core principles discussed here, as long as your CMS supports folder-based organization.
What will you learn in this guide? Here's a sneak peek:
- How to bake a multi-domain Storyblok space
- Tasty spices, aka small but very useful features
- Handling international filling
The Scenario: Managing Multiple Domains
Let’s imagine you have three domains that need to be managed under one hCMS workspace:
- domain-1.com
- domain-2.com
- domain-3.com
Step 1: Organizing Content by Domain
Start by creating folders within your CMS for each domain and fill them with the respective content. In Storyblok, this might look something like this:
With your content neatly organized, the necessary preparations on the CMS side is complete. Now, let’s dive into the code.
Step 2: Configuring Data Fetching in Next.js
In this guide, we'll use Next.js along with Vercel for deployment. The key here is to configure data fetching based on the domain, which will allow your application to serve the correct content dynamically.
Example: Fetching Data for a Specific Domain
async function getData({ slug }: { slug: string }) {
const storyblokApi = getStoryblokApi();
try {
let { data } = await storyblokApi.get(
`cdn/stories/${process.env.NEXT_PUBLIC_BASE_PATH}/${slug}`,
{
version: "draft", // Fetches the draft version of the content
}
);
return {
props: {
story: data ? data.story : false,
key: data ? data.story.id : false,
},
revalidate: 86400, // Revalidate every 24 hours
};
} catch (error) {
redirect("/404"); // Redirect to 404 page if content is not found
}
}
Environment Variable Setup
To ensure your application knows which domain it’s serving, set up an environment variable in your .env
file:
NEXT_PUBLIC_BASE_PATH=domain-1
Step 3: Deploying on Vercel
When deploying to Vercel, you’ll need to create a separate project for each domain. In each project, configure the environment variables accordingly:
-
Project 1:
NEXT_PUBLIC_BASE_PATH=domain-1
-
Project 2:
NEXT_PUBLIC_BASE_PATH=domain-2
-
Project 3:
NEXT_PUBLIC_BASE_PATH=domain-3
This ensures that each domain pulls the correct content from your CMS.
Step 4: Handling Links in Your Application
Example: Link Processing Function
export const linkProcessor = (link: IStoryLink) => {
if (!!process.env.NEXT_PUBLIC_BASE_PATH) {
let correctUrl = link?.story?.full_slug || link.cached_url || link.url;
// Remove base path if link is to the homepage
if (correctUrl === `${process.env.NEXT_PUBLIC_BASE_PATH}/`) {
correctUrl = correctUrl.replace(`${process.env.NEXT_PUBLIC_BASE_PATH}`, '');
} else {
correctUrl = correctUrl.replace(`${process.env.NEXT_PUBLIC_BASE_PATH}/`, '');
}
return correctUrl;
}
return link?.story?.full_slug || link.cached_url || link.url;
};
This function ensures that when users navigate your site, they won’t see URLs like prod-domain.com/domain-1/
but rather the cleaner prod-domain.com/
.
That's all. It's THAT simple. What did you expect, a 10-page guide? Stop reading and go have fun with JavaScript! Happy hacking!
Advanced Considerations
For more sophisticated readers, here are a few other places where you can use this in your project.
Sitemap Generation
First of all, don't forget your roots. In our case, it's a sitemap. The main thing you should do is add this NEXT_PUBLIC_BASE_PATH
to .env
file.
NEXT_PUBLIC_PRODUCTION_DOMAIN=https://prod-domain.com
In your sitemap generation script (generateSitemap.ts
), modify the slug to remove the base path:
const slug = story.full_slug.replace(`${process.env.NEXT_PUBLIC_BASE_PATH}/`, '');
const slugWithoutTrailingSlash = slug
.split('/')
.filter(Boolean)
.join('/');
return `\n<url>\n<loc>${process.env.NEXT_PUBLIC_PRODUCTION_DOMAIN}/${slugWithoutTrailingSlash}</loc>
<lastmod>${publishedAt}</lastmod></url>`;
Configuring Redirects and Rewrites
You can also control redirects, rewrites, or other configurations based on the domain:
switch (process.env.NEXT_PUBLIC_BASE_PATH) {
case 'domain-1':
redirects = [...DEFAULT_REDIRECTS, ...DOMAIN_1_REDIRECTS];
rewrites = DEFAULT_REWRITES;
break;
default:
redirects = DEFAULT_REDIRECTS;
rewrites = DEFAULT_REWRITES;
break;
}
Managing Tracking Scripts
Lastly, use the environment variable to manage domain-specific tracking scripts:
<Script
id="tracking-script"
strategy="lazyOnload"
src="https://cdn.tracking_script.com/script/ididid"
data-domain={
process.env.NEXT_PUBLIC_BASE_PATH === 'domain-1'
? 'first-domain-id'
: 'domain-2.com'
}
/>
This ensures that your tracking data is accurately associated with the correct domain.
Multilanguage
You may ask yourself, "What about multilanguage support?" Fear not! Storyblok has you covered. By following the same principles discussed here, you can easily manage multilanguage content across multiple domains. Simply create folders for each language and domain, and you're good to go.
If you wish for more control over multilanguage settings, you can create a configuration
folder with a languages
subfolder. In this subfolder, create stories for each language with any settings you can imagine. Just fetch this data like we did in the Step 2 example, and you're all set!
Another question you might have is, "Hey, what if some of my domains are multilanguage, and others aren't?" No worries! You can mix and match as needed. Storyblok's flexible structure allows you to adapt to various scenarios seamlessly.
For example, you can create configuration
folder with domains-settings
subfolder which will have stories for each domain with settings like isUsingLocales
and defaultHomepageSlug
:
Also, don't forget about Storyblok built-in conditional fields. They can be a lifesaver when you need to show or hide some fields from content managers.
You can fetch this configuration in your application and use it to determine how to handle multilanguage content for each domain. The possibilities are endless!
That's all, folks!
And there you have it! With Storyblok and a bit of JavaScript magic, you can manage multiple domains under one CMS space with ease. So go forth and build amazing projects that span the digital realm. Happy hacking!
Posted on August 26, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.