Keycloakify

11.15.3 · active · verified Tue Apr 21

Keycloakify is a framework designed to streamline the creation of custom Keycloak user interfaces using React. It acts as a build tool that compiles a React application into a Keycloak-compatible theme, abstracting away the complexities of FreeMarker (FTL) templating. The library is actively maintained, with the current stable version being 11.15.3, and exhibits a fairly rapid release cadence, often pushing several minor or patch updates within a short period. Keycloakify's primary differentiator is its ability to enable modern web development practices (like React) for Keycloak theming, supporting a broad range of Keycloak versions from 11 up to 26 and beyond. This allows developers to leverage familiar tooling and component-based architectures for login, registration, account management, and other Keycloak-provided pages, rather than directly interacting with FreeMarker templates.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates the core structure of a Keycloakify theme: setting up an `KcApp` component to route Keycloak pages, defining a `KcContext` for type safety, and the build command to generate the `.jar` theme file. It includes a simplified `KcLogin` page as an example.

import { lazy, Suspense } from 'react';
import type { PageProps } from 'keycloakify/lib/KcProps';
import { useKcMessage } from 'keycloakify/lib/i18n';
import { createUseKcContext, get } from 'keycloakify';
import type { KcContext } from 'keycloakify';

// 1. Define your custom KcContext extension (optional)
// This example extends the context for a custom 'my-custom-page.ftl'
export type KcContextExtension = {
  customData: string;
};

export type KcContextExtended = KcContext & KcContextExtension;

// 2. Create your custom useKcContext hook
export const { useKcContext } = createUseKcContext<KcContextExtension>();

// 3. Main application component that renders Keycloak pages
export default function KcApp(props: PageProps<KcContextExtended>) {
  const { kcContext } = props;
  const { msg } = useKcMessage();

  // Dynamically set page title
  if (kcContext) {
    document.title = msg("doLogIn"); // Example: set based on a common message key
  }

  // Lazy load page components based on Keycloak's pageId
  const PageComponent = kcContext ? lazy(() => {
    switch (kcContext.pageId) {
      case 'login.ftl': return import('./pages/KcLogin');
      case 'register.ftl': return import('./pages/KcRegister');
      // Add more cases for other Keycloak pages you want to customize
      // For custom pages (e.g., 'my-custom-page.ftl'), ensure they are handled
      case 'my-custom-page.ftl': return import('./pages/MyCustomPage');
      default: return import('./pages/KcDefaultPage'); // Fallback for unhandled pages
    }
  }) : null;

  return (
    <Suspense fallback={<div>Loading Keycloak page...</div>}>
      {kcContext && PageComponent ? <PageComponent {...{ kcContext }} /> : <div>No Keycloak context available.</div>}
    </Suspense>
  );
}

// src/keycloak-theme/pages/KcLogin.tsx (Simplified example)
// import React from 'react';
// import type { PageProps } from 'keycloakify/lib/KcProps';
// import type { KcContextExtended } from '../KcApp'; // Use your extended context
// import { useKcMessage } from 'keycloakify/lib/i18n';

// export default function KcLogin(props: PageProps<KcContextExtended>) {
//   const { kcContext } = props;
//   const { msg } = useKcMessage();

//   return (
//     <div>
//       <h1>{msg('loginTitle')}</h1>
//       <p>Welcome to the custom login page for {kcContext.realm.displayName}!</p>
//       <form action={kcContext.url.loginAction} method='post'>
//         <input type='text' id='username' name='username' placeholder={msg('username')} />
//         <input type='password' id='password' name='password' placeholder={msg('password')} />
//         <button type='submit'>{msg('doLogIn')}</button>
//       </form>
//     </div>
//   );
// }

// package.json (add this script)
/*
{
  "name": "my-keycloak-theme",
  "version": "1.0.0",
  "scripts": {
    "build-keycloak-theme": "keycloakify build"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "keycloakify": "^11.0.0"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.0"
  }
}
*/

view raw JSON →