Can I use this?

Webiny Enterprise license is required to use this feature.

This feature is available since Webiny v5.40.0.

What you’ll learn
  • how to manage tenants via a Headless CMS content model
  • how to programmatically install tenants
  • how to change Admin app theme per tenant at runtime


Out of the box, Webiny provides a very basic module to manage tenants, called Tenant Manager. We’ve talked about this module in the article on Multi-Tenancy. This module allows you to create tenants, tag them, and assign custom domains for website routing (assuming you’re using our Page Builder to build your website). This works well for managing of a small number of tenants. However, if you need to manage hundreds, or even thousands of tenants, that UI will quickly become unsuitable for the task.

Building a tenant management system to satisfy a wide variety of business use cases is difficult, because every business domain has its own definition of a tenant. In some projects, a tenant would be represented by a “Company”, in other projects, tenant would be a “Brand”, etc. Webiny allows you to use the tenancy API to closely integrate its tenants (used for data separation), with your business domain, and have a flawless tenant management experience, which follows your business rules.


In the next couple of sections, we’ll explain how you can build your own tenant manager, which perfectly fits your business and your business terminology, using Headless CMS and the programmatic API for multi-tenancy.

We’ll implement the following items:

  • a content model that represents a tenant in the context of your business domain
  • a GraphQL mutation to execute Webiny tenant creation and installation
  • a custom table column to trigger the GraphQL mutation from the UI
  • apply custom tenant theme (colors and logo) to the Webiny Admin app
Using the Source Code

The full source code for this article is located in the Webiny Examplesexternal link repository. Instructions on setting up the demo source code in your own project are located in the README.mdexternal link file next to the source code.


In this article, we’ll be referring to the full source codeexternal link hosted in the Webiny Examples repository, and only describing the process and the outcomes.

Tenant Content Model

To begin, we need to create a content modelexternal link which represents a business tenant in our system, which we’ll use to create the low-level tenants within Webiny. We’ve created a content model plugin via code, as described in the Define Content Models via Code article.

In our example, we named our model Company, and created a few basic attributes: name, description, theme (with primaryColor, secondaryColor, and logo attributes), and a hidden boolean attribute called isInstalled.

As you develop your system, you will most likely have many more attributes, but this should be enough to describe the concept and the mechanics involved.

When deployed, this content model will appear in the navigation menu under our custom content model groupexternal link, and we will be able to create new Company content entries:

Create and Install a Company Tenant

Now that we have our Company model, we can implement our first use case: Create and Install Tenantexternal link. This use case takes a company id, loads the content entry using the programmatic Headless CMS API, creates an internal Webiny tenant, and installs it. We create a tenant with the id of a Company content entry, to have a 1:1 connection between a Webiny tenant and your business entity.

This use case class encapsulates a business scenario, and you can run it from different parts of Webiny. Our goal is to be able to run this use case by issuing a GraphQL mutation, and so, we need to create a GraphQL schema plugin next.

GraphQL Mutation to Create and Install a Tenant

The following GraphQL schema pluginexternal link exposes a mutation which takes a company id, and runs the CreateAndInstallTenant use case.

GraphQL Mutation to Create and Install TenantsGraphQL Mutation to Create and Install Tenants
(click to enlarge)

This is just one way of running a use case; you could achieve the same by implementing a custom REST route, or a background task, to install a company in a completely asynchronous manner.

Seeding a Tenant

Keep in mind that, if you plan to seed your new tenant with data, depending on the amount of data you’re seeding, you will most likely want to move that logic to a background task, since it’s not limited by a 30 seconds API Gateway request timeout. Tenant creation and installation could still be executed as described in this example, but to seed a tenant, you would invoke an asynchronous background task.

Table Column to Install and Access a Tenant

Now we need to add a piece of UIexternal link, in our example it will be a button, to trigger tenant installation. The table customization API is documented in the Customize Entry List Table Columns article.

Company EntriesCompany Entries
(click to enlarge)

The “Tenant” column we’ve implemented has several purposes. It relies on our model’s isInstalled attribute to determine whether the tenant has been created and installed. If the tenant hasn’t yet been created, it will render a “Install” button, otherwise, it will render a “Manage” button. Clicking the “Manage” button will switch the Admin app to that tenant.

Disable Company Publishing

In this demo, we hide all actions related to content entry publishingexternal link, as we don’t want the Company content entries to be published. We simply keep them in the draft state. This makes it easier to update data programmatically, since you won’t have to deal with content revisions.

The way we do it is, we decorate the usePermission hook, and we always return false for canPublish() permission check on the Company model. This, in turn, affects all the UI that uses permission checks.

Optional Customization

This customization is optional. If you do want to allow Companies to be published, you will have to make some changes on the API side of things. There are pros and cons to both approaches, so do reach out to us on our community slackexternal link and we can discuss the best way forward for your project.

Tenant Theming

Each Company is allowed to have its own brand attributes, like colors, logo, etc. The ApplyTenantThemeexternal link component demonstrates how you can programmatically modify the Admin app theme, when a user is accessing the Admin app for a particular Company.

Custom Tenant ThemeCustom Tenant Theme
(click to enlarge)

In our demo, we’re only showing customization of colors and logo, but following this approach, you can customize pretty much anything related to the UI: navigation menu items, dashboard, columns, even functional plugins.