Skip to content

Local development ​

Reach for the CLI when your theme files should live in your own repo, or you need to hand-edit FreeMarker markup. It runs the editor locally against your files and watches them for changes.

Start a theme project ​

You'll need Node.js 24 and a Java runtime on PATH (the CLI checks for Java 8+; Maven isn't required to run the packaged renderer).

From your application's repo:

bash
npx keycloak-theme-editor init --themes-root ./auth-theme
cd auth-theme
npm start

init scaffolds this and installs the editor as a dev dependency:

text
auth-theme/
  package.json
  package-lock.json
  kc-page.ts
  kc-page-state.ts
  my-theme/
    login/
      theme.properties
      resources/css/custom.css

Rename my-theme to your real theme ID (e.g. acme-login) before you start working, and commit the theme directory, mock files, and lockfile, just exclude node_modules/.

The starter inherits from keycloak.v2. If you hand-edit its custom.css, register it in theme.properties:

properties
parent=keycloak.v2
import=common/keycloak
styles=css/custom.css

The editor opens at http://localhost:4800. Pick up the discovered local theme, make a small change, then Export theme → Save to project and check the diff in Git. Renaming the export doesn't rename the theme's directory: it still saves to wherever it was discovered.

Open an existing theme repo ​

bash
npx keycloak-theme-editor start --themes-root ./auth-theme --port 4800 --no-open

The CLI walks up to five directory levels below --themes-root looking for login/theme.properties, skipping node_modules, .git, and build output. Each one it finds shows up as a variant.

OptionPurpose
--themes-root <dir>Directory to scan. Defaults to the current directory.
--port <number>Local editor port. Defaults to 4800.
--no-openDon't open a browser automatically.

Create acme-login/login/footer.ftl:

ftl
<#macro content>
  <div class="acme-support">
    <a href="https://support.acme.example">${msg("acmeSupport")}</a>
  </div>
</#macro>

Add the message it references, in acme-login/login/messages/messages_en.properties:

properties
acmeSupport=Contact Acme support

And style it, in the stylesheet already listed in theme.properties:

css
.acme-support {
  padding-block: 1rem;
  text-align: center;
}

This replaces the parent's footer macro entirely. If your theme already has a footer, edit it instead so you don't lose its existing links, and add the new message key for every language you support. The CLI regenerates the preview as you save, so you'll see it right away; confirm it on login and password reset, then check the real export.

A word of caution on shared files like template.ftl: overriding one touches every page that uses it. Copy the version that matches your target server, keep the diff small, and re-check it on every Keycloak upgrade.

Preview data for your own scenarios ​

Override sample data in the root kc-page.ts, without touching your real realm:

ts
import { defineConfig } from 'keycloak-theme-editor'

export default defineConfig({
  pages: {
    'login.ftl': {
      realm: { displayName: 'Acme Workspace', registrationAllowed: false },
      client: { clientId: 'acme-web', name: 'Acme Customer Portal' },
    },
  },
})

Or add a named scenario in kc-page-state.ts:

ts
import { defineStates } from 'keycloak-theme-editor'

export default defineStates({
  'login.ftl': {
    'sign-in-error': {
      message: { type: 'error', summary: 'Sign-in failed. Please try again.' },
      login: { username: 'alex@northwind.example' },
    },
  },
})

Use full page IDs (login.ftl, not login). The preview merges these on top of its base mocks: they only affect what you see locally, and don't create clients, enable registration, or otherwise configure Keycloak. Anything involving real JavaScript or a complete login flow still needs a check against a live server.

Check the installed CLI ​

bash
npm ls keycloak-theme-editor
npx keycloak-theme-editor start --help
java -version

These docs match the CLI's current source. If your installed version behaves differently (older releases used --pages and kc-page-story.ts instead of --themes-root and kc-page-state.ts), check --help and the installed version before assuming something's broken. Commit the lockfile change along with your theme change.

There's no headless JAR-build command yet. Use Download JAR in the editor, or package your reviewed files yourself following Keycloak's archive layout. From there: deployment.