Single-Page

Customizing the Single Page Account UI

The present documation page is a transcript of what I explain in this video:

This excerpt is from a video call with the Keycloak team, where we introduced the support for Account SPA customization.

Initializing the Single-Page Account Theme

You've made your mind and opted for the Single Page Account UI? Great, let's start by initializing you theme:

npx keycloakify initialize-account-theme

This command will create the nessesary boilerpate and add to your project dependencies the required dependencies of the latest Account UI.

Before starting the customization, let's make sure that everything works by adding a simple console.log.

src/account/KcPage.tsx
import { lazy } from "react";
import { KcAccountUiLoader } from "@keycloakify/keycloak-account-ui";
import type { KcContext } from "./KcContext";

const KcAccountUi = lazy(() => import("@keycloakify/keycloak-account-ui/KcAccountUi"));

export default function KcPage(props: { kcContext: KcContext }) {
    const { kcContext } = props;
    
    console.log("This is my account theme!");
    
    return <KcAccountUiLoader kcContext={kcContext} KcAccountUi={KcAccountUi} />;
}

Then let's start Keycloak and test it live:

npx keycloakify start-keycloak

Selet Keycloak 25.

Once you get the confirmation message that Keycloak is up and running you can reach the https://my-theme.keycloakify.dev to get redirected to your Login theme. Use the test credentials to authenticate as the test user:

On the next page you'll be provided with a link to the Account pages:

You should be able to see your log statement confirming that you are indeed running your theme.

Compilation of your theme is running in watch mode when using the start-keycloak command, you can eddit your console.log message, save and after a few seconds reload the page, you should see the message updated. After completing the initialization process, it's a good time to commit the changes.

git add -A
git commit -am "Initialize the Single-Page Account Theme"

Basic customization

You can customize some aspect of the account theme witout having to go down at the React component level. If you stick to this level of customization the Keycloak team is able to guarenty that you'll be able to keep your theme compatible with upcoming version of Keycloak with minimal maintenance effort.

Let's see what we can do.

First start by adding a logo file in your src directory somewhere, example:

And pass a reference to it as props of the <KcAccountUiLoader /> component:

src/account/KcPage.tsx
import { lazy } from "react";
import { KcAccountUiLoader } from "@keycloakify/keycloak-account-ui";
import type { KcContext } from "./KcContext";
import myLogoPngUrl from "./assets/my-logo.png";

const KcAccountUi = lazy(() => import("@keycloakify/keycloak-account-ui/KcAccountUi"));

export default function KcPage(props: { kcContext: KcContext }) {
    const { kcContext } = props;

    return (
        <KcAccountUiLoader
            kcContext={kcContext}
            KcAccountUi={KcAccountUi}
            logoUrl={myLogoPngUrl}
        />
    );
}

After saving and reloading you should be able to see that the logo has been updated:

Customizing what sections are availables in the left pannel

Beside changing option in your Keycloak realm configuration you can define what options should be displayed and when in the left pannel. For that you can use the content props of the KcAccountUiLoader component.

You can start by copy/pasting the default content located in node_modules/@keycloakify/keycloak-account-ui/src/public/content.ts

src/account/KcPage.tsx
import { lazy } from "react";
import { KcAccountUiLoader } from "@keycloakify/keycloak-account-ui";
import type { KcContext } from "./KcContext";
import myLogoPngUrl from "./assets/my-logo.png";

const KcAccountUi = lazy(() => import("@keycloakify/keycloak-account-ui/KcAccountUi"));

export default function KcPage(props: { kcContext: KcContext }) {
    const { kcContext } = props;

    return (
        <KcAccountUiLoader
            kcContext={kcContext}
            KcAccountUi={KcAccountUi}
            logoUrl={myLogoPngUrl}
            content={[
                {
                    label: "personalInfo",
                    path: ""
                },
                {
                    label: "accountSecurity",
                    children: [
                        {
                            label: "signingIn",
                            path: "account-security/signing-in"
                        },
                        {
                            label: "deviceActivity",
                            path: "account-security/device-activity"
                        },
                        {
                            label: "linkedAccounts",
                            path: "account-security/linked-accounts",
                            isVisible: "isLinkedAccountsEnabled"
                        }
                    ]
                },
                {
                    label: "applications",
                    path: "applications"
                },
                {
                    label: "groups",
                    path: "groups",
                    isVisible: "isViewGroupsEnabled"
                },
                {
                    label: "resources",
                    path: "resources",
                    isVisible: "isMyResourcesEnabled"
                },
                {
                    label: "oid4vci",
                    path: "oid4vci",
                    isVisible: "isOid4VciEnabled"
                }
            ]}
        />
    );
}

Let's for example comment out the deviceActivity section.

Adding custom CSS

The official way of customizing the look of the Account UI is to overload the PaternFly CSS variables:

But we are aware that this isn't enough for most of you.

Beyond that you can of course import your custom CSS and use the PaternFly utility classes as target but be aware that theses classes can be changed in the future.

Example loading some custom CSS:

Component level customization

To customize the Account theme at the React component level you want to use the eject-page command and slect "account".

npx keycloakify eject-page

After running this command you'll be able to see that the following change has been automatically applied:

src/account/KcPage.tsx
 import { lazy } from "react";
 import { KcAccountUiLoader } from "@keycloakify/keycloak-account-ui";
 import type { KcContext } from "./KcContext";

-const KcAccountUi = lazy(()=> import("@keycloakify/keycloak-account-ui/KcAccountUi"));
+const KcAccountUi = lazy(() => import("./KcAccountUi"));

 export default function KcPage(props: { kcContext: KcContext }) {
     const { kcContext } = props;

     return (
         <KcAccountUiLoader
             kcContext={kcContext}
             KcAccountUi={KcAccountUi}
         />
     );
 }

Also the KcAccountUi component has be copied over from node_modules/@keycloakify/keycloak-account-ui/src/KcAccountUi.tsx to your codebase at src/account/KcAccountUi.tsx.

That what you'll want to each time you'll want to take ownership of some component of the Account UI: Copy the original source file into your codebase and update the absolute imports by relative imports. Let's see in practice how we would eject the routes:

cp node_modules/@keycloakify/keycloak-account-ui/src/routes.tsx src/account/

Then you want to update the absolututes import of the routes to your local routes in KcAccountUI.tsx

- import { routes } from "@keycloakify/keycloak-account-ui/routes";
+ import { router } from "./routes";

And that's it, you now own the routes! Moving forward you can incrementally "eject" the components you need to take ownerhip over by followind the same process.

Be aware: The more components you eject, the more work it will represent to mainain your theme up to date with the future evolution of Keycloak.

Updating keycloak-account-ui to a new version

Each time a new version of Keycloak is released, a new version of @keycloak/keycloak-account-ui is also released with the same version number. This does not nessesarely means that in order to have a custom theme that works with Keycloak 25.0.2 for example you need to use @keycloak/keycloak-account-ui@25.0.2. Actually it's very likely that you theme will still work with Keycloak 26, however at some point your theme will break with future version of Keycloak and you'll have to upgrade.

Keycloakify does not use the NPM package @keycloak/keycloak-account-ui but an automatically repackaged distribution of it: @keycloakify/keycloak-account-ui.

So, when comes the time to upgrade you want to navigate to:

And look in the README in the installation section:

You want to copy and paste the dependencies into the package.json of your Keycloakify project.

The readme is generated automatically, you can trust that is always up do date.

You migh wonder why there's only RC releases of @keycloakify/keycloak-account-ui, it's because we want to match the version number of the upstream package @keycloak/keycloak-account-ui but still be able to publish update when minor changes on the re-packaging distribution is needed.

Last updated