Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions js/apps/admin-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ npm i @keycloak/keycloak-admin-ui

## Usage

To use these pages you'll need to add `KeycloakProvider` in your component hierarchy to setup what client, realm and url to use.
Wrap your app with `KeycloakProvider` from `@keycloak/keycloak-ui-shared` so the realm, client, and server URLs are available. Any subtree that calls `useAdminClient()` (or otherwise needs the Keycloak Admin Client) must also be wrapped with `AdminClientProvider` from this package, **inside** `KeycloakProvider`. `AdminClientProvider` initializes the admin client from the authenticated Keycloak instance and exposes it through `AdminClientContext`.

```jsx
import { KeycloakProvider } from "@keycloak/keycloak-ui-shared";
import { AdminClientProvider } from "@keycloak/keycloak-admin-ui";
Comment on lines +17 to +21

//...

<KeycloakProvider environment={{
authServerUrl: "http://localhost:8080",
realm: "master",
clientId: "security-admin-console"
}}>
{/* rest of you application */}
<KeycloakProvider environment={environment}>
<AdminClientProvider>
{/* components that use useAdminClient() */}
</AdminClientProvider>
Comment on lines +17 to +28
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section instructs users to call useAdminClient(), but that hook isn’t currently part of the package’s public exports from @keycloak/keycloak-admin-ui (it’s not re-exported from src/index.ts). Please either adjust the docs to match the actual public API, or re-export the hook so the documented import path works.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, seems relevant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is.... fixed it

</KeycloakProvider>
```

The `environment` object must include the admin console fields expected by this library (see `AdminEnvironment` in the package), not only `BaseEnvironment`.

### Translation

For the translation we use `react-i18next` you can [set it up](https://react.i18next.com/) as described on their website.
Expand Down
39 changes: 11 additions & 28 deletions js/apps/admin-ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import {
mainPageContentId,
useEnvironment,
} from "@keycloak/keycloak-ui-shared";
import { Flex, FlexItem, Page } from "@patternfly/react-core";
import { PropsWithChildren, Suspense, useEffect, useState } from "react";
import { Outlet } from "react-router-dom";

import {
ErrorBoundaryFallback,
ErrorBoundaryProvider,
KeycloakSpinner,
mainPageContentId,
} from "@keycloak/keycloak-ui-shared";
import { Flex, FlexItem, Page } from "@patternfly/react-core";
import { PropsWithChildren, Suspense, useEffect } from "react";
import { Outlet } from "react-router-dom";
import { AdminClientProvider } from "./admin-client";
import { Header } from "./PageHeader";
import { PageNav } from "./PageNav";
import { AdminClientContext, initAdminClient } from "./admin-client";
import { PageBreadCrumbs } from "./components/bread-crumb/PageBreadCrumbs";
import { ErrorRenderer } from "./components/error/ErrorRenderer";
import { RecentRealmsProvider } from "./context/RecentRealms";
import { AccessContextProvider } from "./context/access/Access";
import { RealmContextProvider } from "./context/realm-context/RealmContext";
import { ServerInfoProvider } from "./context/server-info/ServerInfoProvider";
import { WhoAmIContextProvider } from "./context/whoami/WhoAmI";
import type { Environment } from "./environment";
import { SubGroups } from "./groups/SubGroupsContext";
import { AuthWall } from "./root/AuthWall";
import { Banners } from "./Banners";
Expand All @@ -46,25 +40,14 @@
);

export const App = () => {
const { keycloak, environment } = useEnvironment<Environment>();
const [adminClient, setAdminClient] = useState<KeycloakAdminClient>();

const hrefEndsWithHashSlash = location.href.endsWith("#/");
useEffect(() => {
const fragment = "#/";
if (window.location.href.endsWith(fragment)) {
const newPath = window.location.pathname.replace(fragment, "");
window.history.replaceState(null, "", newPath);
}
const init = async () => {
const client = await initAdminClient(keycloak, environment);
setAdminClient(client);
};
init().catch(console.error);
}, [environment, keycloak]);
if (!hrefEndsWithHashSlash) return;
history.replaceState(null, "", location.pathname);
}, [hrefEndsWithHashSlash, location.pathname]);

Check warning on line 47 in js/apps/admin-ui/src/App.tsx

View workflow job for this annotation

GitHub Actions / Admin UI

React Hook useEffect has an unnecessary dependency: 'location.pathname'. Either exclude it or remove the dependency array. Outer scope values like 'location.pathname' aren't valid dependencies because mutating them doesn't re-render the component

if (!adminClient) return <KeycloakSpinner />;
return (
<AdminClientContext.Provider value={{ keycloak, adminClient }}>
<AdminClientProvider>
<AppContexts>
<Flex
direction={{ default: "column" }}
Expand Down Expand Up @@ -94,6 +77,6 @@
</FlexItem>
</Flex>
</AppContexts>
</AdminClientContext.Provider>
</AdminClientProvider>
);
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import KeycloakAdminClient from "@keycloak/keycloak-admin-client";
import {
createNamedContext,
KeycloakSpinner,
useEnvironment,
useRequiredContext,
} from "@keycloak/keycloak-ui-shared";
import type Keycloak from "keycloak-js";
import { PropsWithChildren, useEffect, useState } from "react";

import type { Environment } from "./environment";

export type AdminClientProps = {
Expand Down Expand Up @@ -39,3 +43,23 @@ export async function initAdminClient(

return adminClient;
}

export const AdminClientProvider = ({ children }: PropsWithChildren) => {
const { keycloak, environment } = useEnvironment<Environment>();
const [adminClient, setAdminClient] = useState<KeycloakAdminClient>();

useEffect(() => {
const init = async () => {
const client = await initAdminClient(keycloak, environment);
setAdminClient(client);
};
init().catch(console.error);
}, [environment, keycloak]);

if (!adminClient) return <KeycloakSpinner />;
return (
<AdminClientContext.Provider value={{ keycloak, adminClient }}>
{children}
</AdminClientContext.Provider>
);
};
7 changes: 6 additions & 1 deletion js/apps/admin-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,12 @@ export { PageNotFoundSection } from "./PageNotFoundSection";
export { App as AdminUi } from "./App";
export type { Environment as AdminEnvironment } from "./environment";
export { KeycloakProvider, useEnvironment } from "@keycloak/keycloak-ui-shared";
export { AdminClientContext, initAdminClient } from "./admin-client";
export {
AdminClientContext,
AdminClientProvider,
initAdminClient,
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package README references useAdminClient(), but useAdminClient is not re-exported from the package entrypoint (src/index.ts). Consumers importing from @keycloak/keycloak-admin-ui won’t be able to access it as documented. Either re-export useAdminClient here, or update the README to use only the public exports.

Suggested change
initAdminClient,
initAdminClient,
useAdminClient,

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, seems relevant.

useAdminClient,
} from "./admin-client";
export { AppContexts } from "./App";
export * as PermissionsConfigurationSection from "./permissions-configuration/PermissionsConfigurationSection";
export { routes } from "./routes";
Loading