I'm working on a multi-tenant Next.js application where I'm experiencing an issue with metadata generation. When a valid subdomain exists and the tenant is found, everything works perfectly - the app renders and metadata is applied correctly. However, when the tenant validation fails and conditional rendering is triggered, the metadata for the invalid tenant doesn't load unless I make a code change that triggers Next.js hot reload - only then does the metadata appear. (Please be nice, I'm new in Nextjs lmao)
layout.tsx:
import type { Metadata } from "next";
import "./globals.css";
import { getTenantData } from "@/lib/utils";
import MissingSubdomainError from "@/components/errors/MissingSubdomainError";
import InvalidTenantError from "@/components/errors/InvalidTenantError";
import { TenantProvider } from "@/components/providers/tenant-provider";
export async function generateMetadata(): Promise<Metadata> {
const { subdomain, tenant } = await getTenantData();
if (!subdomain) {
return {
title: "Subdominio no definido",
description: "No se proporcionó un subdominio válido",
};
}
if (!tenant) {
return {
title: "Tenant inválido",
description: `No se encontró ningún tenant llamado "${subdomain}"`,
};
}
return {
title: tenant.name,
description: `Sitio de ${tenant.name}`,
};
}
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const { subdomain, tenant } = await getTenantData();
if (!subdomain) return <MissingSubdomainError />;
if (!tenant) return <InvalidTenantError subdomain={subdomain} />;
return (
<html lang="es">
<body>
<TenantProvider subdomain={subdomain} tenantData={tenant}>
{children}
</TenantProvider>
</body>
</html>
);
}
InvalidTenantError.tsx:
export default function InvalidTenantError({
subdomain,
}: {
subdomain: string;
}) {
return (
<html lang="es">
<body className="flex items-center justify-center min-h-screen bg-gray-100">
<div className="bg-white p-8 rounded-xl shadow-lg text-center max-w-md">
<h1 className="text-3xl font-bold text-red-600 mb-4">
Subdominio inválido
</h1>
<p className="text-gray-700 mb-2">
El subdominio{" "}
<span className="font-semibold text-gray-900">{subdomain}</span> no
está registrado.
</p>
<p className="text-gray-600 mb-4">
Verifica que lo hayas escrito correctamente. Por ejemplo:
</p>
<code className="bg-gray-100 px-2 py-1 rounded text-sm text-gray-800">
empresa.localhost
</code>
<p className="text-sm text-gray-500 mt-4">
Si crees que esto es un error o necesitas ayuda, contáctanos.
</p>
</div>
</body>
</html>
);
}
utils.ts:
import { headers } from "next/headers";
import { prisma } from "./prisma";
export async function validateTenant(subdomain: string) {
try {
const tenant = await prisma.tenant.findUnique({
where: {
subdomain: subdomain,
active: true,
},
});
return tenant;
} catch (error) {
console.error("Error validating tenant:", error);
return null;
}
}
export async function getTenantData() {
const headersList = await headers();
const subdomain = headersList.get("x-tenant");
if (!subdomain) {
return { subdomain: null, tenant: null };
}
const tenant = await validateTenant(subdomain);
return { subdomain, tenant };
}
package.json:
{
"name": "tenanttest",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"build": "prisma generate && next build",
"start": "next start",
"lint": "next lint",
"db:seed": "tsx prisma/seed.ts"
},
"dependencies": {
"@prisma/client": "^6.8.2",
"next": "15.3.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"zustand": "^5.0.5"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.2",
"prisma": "^6.8.2",
"tailwindcss": "^4",
"tsx": "^4.19.4",
"typescript": "^5"
}
}
Metadata working:
Metadata not working:

