Loading...
Loading...
Apply when building VTEX IO apps that must work correctly in multi-binding stores where bindings use path prefixes (e.g. store.com/us/, store.com/br/). Covers rootPath extraction from x-vtex-root-path header, useRuntime().rootPath in React, URL construction in backends, link generation, asset paths, and API route considerations. Use when the app breaks or produces wrong URLs in cross-border or multi-binding setups.
npx skill4agent add vtex/skills vtex-io-rootpathstore.com/us/store.com/br/store.com/mx/vtex-io-service-appsvtex-io-service-paths-and-cdnstore.com/us/store.com/br/rootPathstore.usstore.com.brrootPath/rootPathx-vtex-root-pathpreparectx.state.rootPathctx.state.rootPathVary: x-vtex-root-pathuseRuntime().rootPathvtex.render-runtime/us/rootPathrootPathrootPath/my-account/orders/br//${slug}rootPathnavigate()rootPath// Backend: use ctx.state.rootPath (parsed by prepare middleware)
const { rootPath, forwardedHost } = ctx.state;
const canonicalUrl = `https://${forwardedHost}${rootPath}/product/${slug}`;
const sitemapEntry = `${rootPath}/${categoryPath}`;// Frontend: use runtime hook
import { useRuntime } from "vtex.render-runtime";
const MyLink = ({ slug }: { slug: string }) => {
const { rootPath } = useRuntime();
return <a href={`${rootPath}/product/${slug}`}>View product</a>;
};// Backend: missing rootPath — breaks in multi-binding
const canonicalUrl = `https://${host}/product/${slug}`
// Frontend: hardcoded path
const MyLink = ({ slug }: { slug: string }) => {
return <a href={`/product/${slug}`}>View product</a>
}rootPath"/"//product/shoesrootPath === "/"""//path/pathrootPath + "/" + pathrootPath === "/"// In the prepare middleware, sanitize before storing on state:
let rootPath = ctx.get("x-vtex-root-path");
if (rootPath && !rootPath.startsWith("/")) {
rootPath = `/${rootPath}`;
}
if (rootPath === "/") {
rootPath = "";
}
ctx.state.rootPath = rootPath;
// Downstream: ctx.state.rootPath is already sanitized
const { rootPath } = ctx.state;
const url = `${rootPath}/${path}`;const rootPath = ctx.get("x-vtex-root-path") || "/";
const url = `${rootPath}/${path}`; // Produces "//path" for default bindingrootPathState// node/typings.d.ts
declare global {
interface State extends RecorderState {
binding: Binding;
rootPath: string;
forwardedHost: string;
forwardedPath: string;
isCrossBorder: boolean;
matchingBindings: Binding[];
}
type Context = ServiceContext<Clients, State>;
}preparex-vtex-root-pathVary// node/middlewares/prepare.ts
const FORWARDED_HOST_HEADER = "x-forwarded-host";
const VTEX_ROOT_PATH_HEADER = "x-vtex-root-path";
export async function prepare(ctx: Context, next: () => Promise<void>) {
const forwardedHost = ctx.get(FORWARDED_HOST_HEADER);
let rootPath = ctx.get(VTEX_ROOT_PATH_HEADER);
// Defend against malformed root path — must start with /
if (rootPath && !rootPath.startsWith("/")) {
rootPath = `/${rootPath}`;
}
// Normalize "/" to "" to avoid double slashes in URL construction
if (rootPath === "/") {
rootPath = "";
}
const [forwardedPath] = ctx.get("x-forwarded-path").split("?");
ctx.state = {
...ctx.state,
forwardedHost,
forwardedPath,
rootPath,
// ... resolve binding, matchingBindings, etc.
};
await next();
// Vary on these headers so CDN caches separate responses per binding
ctx.vary(FORWARDED_HOST_HEADER);
ctx.vary(VTEX_ROOT_PATH_HEADER);
}ctx.state.rootPath// node/middlewares/generateSitemap.ts
export async function generateSitemap(ctx: Context, next: () => Promise<void>) {
const { rootPath, binding } = ctx.state;
const canonicalUrl = `https://${ctx.state.forwardedHost}${rootPath}/${slug}`;
// ...
}import { useRuntime } from "vtex.render-runtime";
function usePrefixedPath(path: string): string {
const { rootPath = "" } = useRuntime();
const prefix = rootPath === "/" ? "" : rootPath;
return `${prefix}${path.startsWith("/") ? path : `/${path}`}`;
}const { rootPath, binding } = useRuntime();
// binding.id — current binding ID
// binding.canonicalBaseAddress — e.g. "store.com/br"
// rootPath — e.g. "/br"
// When calling backend APIs, the platform handles rootPath automatically
// for IO-internal calls. For external URLs or custom redirects, prefix manually.rootPathrootPathrootPath === "/"/path//path/us//br/rootPathx-vtex-root-pathVary: x-vtex-root-pathVary: x-forwarded-hostpreparex-vtex-root-pathctx.state.rootPathuseRuntime()Vary: x-vtex-root-pathVary: x-forwarded-hostrootPathrootPath === "/"""/us//br/