{"id":28487528,"url":"https://github.com/payloadcms/localized-multitenant","last_synced_at":"2025-07-19T04:34:11.789Z","repository":{"id":293698697,"uuid":"984251712","full_name":"payloadcms/localized-multitenant","owner":"payloadcms","description":null,"archived":false,"fork":false,"pushed_at":"2025-05-16T16:42:46.000Z","size":110,"stargazers_count":6,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-15T06:55:33.735Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/payloadcms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-05-15T16:19:25.000Z","updated_at":"2025-06-26T11:39:52.000Z","dependencies_parsed_at":"2025-05-17T06:46:01.811Z","dependency_job_id":null,"html_url":"https://github.com/payloadcms/localized-multitenant","commit_stats":null,"previous_names":["payloadcms/localized-multitenant"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/payloadcms/localized-multitenant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/payloadcms%2Flocalized-multitenant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/payloadcms%2Flocalized-multitenant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/payloadcms%2Flocalized-multitenant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/payloadcms%2Flocalized-multitenant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/payloadcms","download_url":"https://codeload.github.com/payloadcms/localized-multitenant/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/payloadcms%2Flocalized-multitenant/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265889024,"owners_count":23844537,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-08T04:31:49.068Z","updated_at":"2025-07-19T04:34:11.752Z","avatar_url":"https://github.com/payloadcms.png","language":"TypeScript","readme":"# Multi-Tenant with Localization Example\n\nThis repo demonstrates how to implement multi-tenancy _and_ localization using:\n\n- [Payload](https://github.com/payloadcms/payload)\n- `@payloadcms/plugin-multi-tenant`\n- A single Next.js frontend app\n\nIt allows you to serve multiple tenants (e.g. `gold.localhost`, `silver.localhost`) with localized URLs (e.g. `/en`, `/fr`) from one codebase.\n\n## Quick Start\n\nTo spin up this example locally, follow these steps:\n\n1. `cp .env.example .env` to copy the example environment variables\n\n2. `pnpm dev`, `yarn dev` or `npm run dev` to start the server\n   - Press `y` when prompted to seed the database\n3. `open http://gold.localhost:3000` to access the home page (note: you can also go to `silver.` or `bronze.`)\n4. `open http://localhost:3000/admin` to access the admin panel (note: adding `/admin` to the custom domains will also take you to the admin panel)\n\n### Login credentials\n\nLogin with email `demo@payloadcms.com` and password `demo`\n\n## Testing Domains\n\nFor the domain portion of the example to function properly, you will need to add the following entries to your system's `/etc/hosts` file:\n\n```\n127.0.0.1 gold.localhost silver.localhost bronze.localhost\n```\n\n# Code Walkthrough - Key Concepts \u0026 Files\n\n### 1. Payload Configuration\n\n**File:** `payload.config.ts`\n\n**Localization setup:**\n\nThe `localization` field defines supported locales (e.g. `['en', 'fr']`) and default locale.\n\nThis enables Payload collections and globals to support localized fields and content.\n\n```ts\nlocalization: {\n  locales: ['en', 'fr'],\n  defaultLocale: 'en',\n  fallback: true,\n},\n```\n\n**Multi-Tenant Plugin**\n\nThe `@payloadcms/plugin-multi-tenant` plugin is installed and passed to Payload via `plugins`.\n\n```ts\nplugins: [\n  multiTenant({\n    // plugin options (e.g., tenant collection, defaults)\n  }),\n],\n```\n\n## 2. Next.js Rewrites\n\n**File:** `next.config.js`\n\nThis file rewrites incoming URLs based on domain and path to match your app directory routing structure.\n\n```mjs\nasync rewrites() {\n    return [\n      {\n        source: '/((?!admin|api)):locale/:path*',\n        destination: '/:tenant/:locale/:path*',\n        has: [\n          {\n            type: 'host',\n            value: '(?\u003ctenant\u003e.*)',\n          },\n        ],\n      },\n    ]\n  },\n```\n\n### How it works:\n\n- Extracts the tenant from the domain (e.g. `gold.localhost`)\n- Restructures URLs like `/en/about` into internal paths like `/:tenant/:locale/:path*`\n- Allows your app directory to cleanly handle tenants and locales via nested dynamic routes\n\n## 3. Next.js App Routing Structure\n\n**Folder:** `/app`\n\n- `/app/[tenant]` – captures tenant from rewritten URL\n- `/app/[tenant]/[locale]` – captures locale from URL\n- `/app/[tenant]/[locale]/[...slug]` – handles all pages under the locale\n\nThis allows paths like to be transformed into the app structure:\n\n```bash\nhttp://gold.localhost:3000/en/about\n→ /app/gold/en/about → tenant = \"gold\", locale = \"en\", slug = \"about\"\n```\n\n## 4. Page Logic\n\n**File:** `/app/[tenant]/[locale]/[...slug]/page.tsx`\n\nThis is the main entry point for rendering localized tenant content. Within this file it does the following:\n\n- Reads tenant, locale, and slug from the URL\n- Fetches tenant-specific config and localized content from Payload\n- Renders dynamic pages for each tenant/locale combo\n\nYou can customize this file to fetch and render any kind of tenant-aware, locale-aware data (e.g. pages, navigation, layout).\n\n## 5. Putting It All Together\n\n1. A user visits `http://gold.localhost:3000/fr/about`\n2. The Next.js rewrite rule extracts:\n   - Tenant from domain (`gold`)\n   - Locale and path from URL (`fr`, `about`)\n3. The URL is rewritten internally to `/gold/fr/about`\n4. Next.js matches the route via `/app/[tenant]/[locale]/[...slug]/page.tsx` in the app directory\n5. `page.tsx` uses the tenant, locale, and slug params to fetch content from Payload\n6. The requested localized content for the correct tenant is rendered\n\n## Summary of Key Files\n\n| File / Folder                               | Purpose                                              |\n| ------------------------------------------- | ---------------------------------------------------- |\n| `payload.config.ts`                         | Sets up localization + multi-tenant plugin           |\n| `next.config.js`                            | Rewrites URLs based on domain and locale             |\n| `/app/[tenant]/[locale]/[...slug]/page.tsx` | Fetches and renders tenant + locale-specific content |\n| `/app/[tenant]/[locale]/[...slug]`          | Directory structure that mirrors rewritten URL paths |\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpayloadcms%2Flocalized-multitenant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpayloadcms%2Flocalized-multitenant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpayloadcms%2Flocalized-multitenant/lists"}