Enabling Virtual Folders in Sitecore AI at the Next.js Layer with Constellation4Sitecore
Author
Roberto Armas
Date Published

In many Sitecore implementations, virtual folders are not just a content-tree convenience. They often support real delivery scenarios that matter to marketing and platform teams.
A common example is the need to launch a microsite under the same domain, using a path such as /microsite, /campaign, or /events, instead of provisioning an entirely separate hostname. Companies often want these experiences to live inside the same brand domain for SEO continuity, governance, analytics consistency, and simpler campaign rollout. In practice, that means delivering a focused experience like samedomain/microsite while still managing it as a distinct structure in Sitecore. The challenge is that Experience Edge applies strict site-definition rules, and by default it does not support multiple site items pointing to the same start item; the documented exception is language-based and still depends on distinct hostnames per language. That pushes teams toward hostname-based resolution instead of path-based, same-domain deployments.
That is exactly where virtual folders become valuable. They let teams organize content in a way that supports separate site sections, campaigns, or microsites without forcing those structural containers into the public URL. In other words, they preserve authoring structure while keeping the frontend URL strategy clean.
There is just one problem: Sitecore’s own documentation is explicit that “Experience Edge doesn't support virtual folders.”
I want to be transparent about where this solution really lives. This is not native virtual folder support coming directly from Sitecore AI. The solution is implemented with Constellation4Sitecore, a third-party extension around the Sitecore Content SDK, and the resolution happens at the Next.js level.
From my perspective, that is actually the most important part of the story. The goal was not to force Experience Edge to understand virtual folders differently. Instead, the idea was to solve the delivery problem in a practical way by treating virtual-folder paths as separate site-level entries and resolving them in the Next.js request pipeline. That means the heavy lifting happens in the application layer, before normal page rendering, while the content structure in Sitecore stays intact.
The Problem: Site Resolution Is Hostname-Based
If you have worked with Sitecore AI and headless Next.js applications, you have probably noticed that multisite resolution is primarily designed around hostnames.
That works well when each site has its own domain or subdomain. For example, if one site lives at www.company.com and another lives at events.company.com, the Sitecore Content SDK multisite pipeline can resolve those as separate site experiences without much trouble.
The challenge appears when the business wants something different: a distinct experience under the same domain, using a path such as /microsite, /events, or /campaign. In those scenarios, teams are not trying to launch a separate hostname. They are trying to deliver a site-like experience inside the same domain space.
That is where the default pattern starts to feel limiting.
With the standard multisite approach, the other site is typically resolved by assigning it a different hostname. If you want to keep everything under the same domain, that option does not really help. The platform can distinguish sites easily by host, but not as naturally by path in the same way teams often need for microsites or campaign sections.
This is where site-level virtual folders become important.
The Better Place to Solve It: The Next.js Layer
The more practical way to approach the problem is to treat it as a URL mapping concern, not a content-fetching concern.
That means doing the expensive work at build time, generating metadata about virtual folders once, and then using that metadata at request time in the Next.js proxy pipeline before the page is rendered.
Conceptually, the request flow looks like this:
1Browser Request2 │3 ▼4LocaleProxy5 │6 ▼7MultisiteProxy8 │9 ▼10VirtualFolderProxy11 │12 ▼13RedirectsProxy14 │15 ▼16PersonalizeProxy17 │18 ▼19Page Render
Ideally, it would be nice that OOTB MultisiteProxy (Content-SDK) resolves the new website as virtual folder.
That placement matters. The virtual-folder resolver runs after locale and multisite resolution, so it already knows which site and locale the request belongs to. It runs before redirects and personalization, so those features operate against the resolved path rather than the raw structural path.
How the Constellation4Sitecore Approach Works
1. Build time: generate a virtual folder map
The key insight is to move the tree-walking out of runtime completely.
During the Sitecore CLI build pipeline, generateVirtualFolders() creates a .sitecore/virtual-folders.json artifact. This file contains the metadata needed to understand which paths are virtual folders and how they map to the underlying content structure.
1// sitecore.cli.config.ts2import { defineCliConfig } from '@sitecore-content-sdk/nextjs/config-cli';3import { generateVirtualFolders } from '@constellation4sitecore-content-sdk/nextjs/tools';4import scConfig from './sitecore.config';5import c4sConfig from './constellation4sitecore.config';67export default defineCliConfig({8 config: scConfig,9 build: {10 commands: [11 generateMetadata(),12 generateSites(),13 generateVirtualFolders({ c4sConfig }),14 extractFiles(),15 writeImportMap({ paths: ['src/components'] }),16 ],17 },18});
Instead of asking Experience Edge or GraphQL to rediscover the structure on every request, the app now has a precomputed map ready to use.
2. Request time: resolve the incoming URL in the proxy pipeline
At request time, VirtualFolderProxy consumes the generated JSON and rewrites the incoming path before the page is fetched.
1// src/proxy.ts2import { NextRequest } from 'next/server';3import { defineProxy } from '@sitecore-content-sdk/nextjs/proxy';4import virtualFolders from '.sitecore/virtual-folders.json';5import c4sConfig from './constellation4sitecore.config';6import scConfig from './sitecore.config';7import sites from '.sitecore/sites.json';89import { VirtualFolderProxy } from '@constellation4sitecore-content-sdk/nextjs/proxy';1011// existing proxies12import { locale } from 'content-sdk....';13import { multisite } from 'content-sdk....';14import { redirects } from 'content-sdk....';15import { personalize } from 'content-sdk....';1617const virtualFolder = new VirtualFolderProxy({18 sites,19 ...scConfig.api.edge,20 virtualFolders,21 c4sConfig,22});2324export default function proxy(req: NextRequest) {25 return defineProxy(26 locale,27 multisite,28 virtualFolder,29 redirects,30 personalize31 ).exec(req);32}
The important part here is that the page component itself does not need special virtual-folder logic. Your standard Content SDK App Router pattern can stay intact.
3. SEO: keep sitemap and robots aligned with the rewritten URLs
Virtual folders often represent distinct site sections, campaigns, or microsites that need SEO artifacts to match the final public URLs.
1// src/app/api/[virtualFolder]/sitemap/route.ts2import { createSitemapRouteHandler } from '@constellation4sitecore-content-sdk/nextjs/route-handler';3import sites from '.sitecore/virtual-folders.json';4import client from 'lib/sitecore-client';56export const { GET } = createSitemapRouteHandler({ client, sites });
1// src/app/api/[virtualFolder]/robots/route.ts2import { createRobotsRouteHandler } from '@constellation4sitecore-content-sdk/nextjs/route-handler';3import sites from '.sitecore/virtual-folders.json';4import client from 'lib/sitecore-client';56export const { GET } = createRobotsRouteHandler({ client, sites });
That keeps runtime URL resolution and SEO output driven by the same source of truth.
How to Add It to a Project
From an implementation perspective, this is one of the strongest parts of the approach: it is mostly wiring, not custom platform surgery.
Step 1: install the packages
1npm install @constellation4sitecore-content-sdk/nextjs // 2.0 version
Step 2: add Constellation4Sitecore configuration
1// constellation4sitecore.config.ts2import { defineConfig } from '@constellation4sitecore-content-sdk/nextjs/config';34export default defineConfig({5 virtualFolders: {6 enabled: process.env.VIRTUAL_FOLDERS_ENABLED === 'true',7 },8});910// OR11export default defineConfig({}); // Fallback config considers VIRTUAL_FOLDERS_ENABLED Env Variable12// By default is set to false.
That feature flag is useful because it lets you enable or disable the behavior per environment.
Step 3: wire generateVirtualFolders() into the CLI build
This is the build-time step that makes the rest of the solution lightweight at runtime.
1import { defineCliConfig } from '@sitecore-content-sdk/nextjs/config-cli';2import {3 generateSites,4 generateMetadata,5 extractFiles,6 writeImportMap,7} from '@sitecore-content-sdk/nextjs/tools';8import { generateVirtualFolders } from "@constellation4sitecore-content-sdk/nextjs/tools";9import scConfig from './sitecore.config';10import c4sConfig from './constellation4sitecore.config'; // Import Config1112export default defineCliConfig({13 config: scConfig,14 build: {15 commands: [16 generateMetadata(),17 generateSites(),18 generateVirtualFolders({ c4sConfig }), // New Line19 extractFiles(),20 writeImportMap({21 paths: ['src/components'],22 }),23 ],24 },25 componentMap: {26 paths: ['src/components'],27 exclude: ['src/components/content-sdk/*'],28 },29});30
Step 4: add VirtualFolderProxy to the proxy chain
This is the operational heart of the solution. It should run after locale and multisite resolution and before redirects and personalization.
Step 6: optionally add sitemap and robots route handlers
If your virtual folders represent public sections that need independent discovery or crawl directives, add the route handlers so the SEO layer stays consistent with the resolved URLs.
Conclusion
The deeper architectural point is this: virtual folder support is fundamentally a routing problem, not a rendering problem. Once you see it that way, build-time metadata plus request-time rewriting becomes a very natural fit.
By shifting virtual folder resolution to the Next.js layer, and by doing the structural work at build time instead of request time, you can preserve clean SEO-friendly URLs, support same-domain microsite scenarios, and keep the content tree organized for authors.
And the best part is that there is already a practical third-party option available for this: Constellation4Sitecore. Virtual folder support is now part of that ecosystem through generateVirtualFolders, VirtualFolderProxy, and supporting route handlers.
Github link