# Theme Variants

Theme variant enables you to create multiples Keycloak theme with a single codebase.

{% tabs %}
{% tab title="Vite" %}
{% code title="vite.config.ts" %}

```typescript
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { keycloakify } from "keycloakify/vite-plugin";

export default defineConfig({
  plugins: [
    react(),
    keycloakify({
      themeName: ["keycloakify-starter", "keycloakify-starter-variant-1"],
    }),
  ],
});
```

{% endcode %}
{% endtab %}

{% tab title="Webpack" %}
{% code title="package.json" %}

```json
{
  "keycloakify": {
    "themeName": ["keycloakify-starter", "keycloakify-starter-variant-1"]
  }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

This will make the theme variant appear in the Keycloak admin select input:

<figure><img src="https://content.gitbook.com/content/N2jP04ve5XD0mhCzbqnp/blobs/Bj2KykUn3b3YNfOJqJja/image.png" alt=""><figcaption></figcaption></figure>

In your code you'll be able to load different styles based on the value of `kcContext.themeName`:

<figure><img src="https://content.gitbook.com/content/N2jP04ve5XD0mhCzbqnp/blobs/ZhrEmwzUjjl8QTgtkba5/image.png" alt=""><figcaption><p>NOTE: You need to <code>run npm run dev</code>, <code>npm run storybook</code> or <code>npm run build-keycloak-theme</code> for the types to be updated.</p></figcaption></figure>

{% embed url="<https://youtu.be/Nkoz1iD-HOA>" %}
Tutorial video
{% endembed %}

## Different text for each of your theme variants

Keycloakify lets you provide custom tranlations on a per-theme variant basis.

{% hint style="info" %}
Read [this](https://doc-old.keycloakify.dev/documentation/v11/i18n/adding-new-translation-messages-or-changing-the-default-ones) first for context.
{% endhint %}

Example:

<pre class="language-typescript"><code class="lang-typescript">import { i18nBuilder } from "keycloakify/login";
import type { ThemeName } from "../kc.gen";

/** @see: https://docs.keycloakify.dev/i18n */
const { useI18n, ofTypeI18n } = i18nBuilder
    .withThemeName&#x3C;ThemeName>()
    .withExtraLanguages({ /* ... */ })
    .withCustomTranslations({
        en: {
            doLogIn: "Log in!",
<strong>            loginAccountTitle: {
</strong><strong>                "my-theme-1": "Log in to your ACME1 account",
</strong><strong>                "my-theme-2": "Log in to your ACME2 account"
</strong><strong>            }
</strong>        },
        // cspell: disable
        fr: {
            doLogIn: "Se connecter!",
<strong>            loginAccountTitle: {
</strong><strong>                "my-theme-1": "Connectez-vous à votre compte ACME1",
</strong><strong>                "my-theme-2": "Connectez-vous à votre compte ACME2"
</strong><strong>            }
</strong>        }
        // cspell: enable
    })
    .build();

type I18n = typeof ofTypeI18n;

export { useI18n, type I18n };

</code></pre>

<figure><img src="https://3648752418-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FN2jP04ve5XD0mhCzbqnp%2Fuploads%2FGro9EN7VoAzGTwcly2Vm%2Fimage.png?alt=media&#x26;token=f06dc6b9-130a-4ba5-bd6d-83453eb493cf" alt=""><figcaption><p>"my-theme-1" view</p></figcaption></figure>

<figure><img src="https://3648752418-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FN2jP04ve5XD0mhCzbqnp%2Fuploads%2FsBjHB4AiwCe55GMc0hbZ%2Fimage.png?alt=media&#x26;token=ebc21a03-3c53-4bca-80b1-e7ddbef13aac" alt=""><figcaption><p>"my-theme-2" view</p></figcaption></figure>

## Email theme

[Your emails](#email-theme) can be adapted based on the active theme variant.\
In the `.ftl` files located in the `src/email` directory, you can use the FreeMarker variable: `xKeycloakify.themeName`.\
This variable holds the name of the currently enabled theme as a string, for example: `"my-theme-1"`, `"my-theme-2"`, etc.
