Skip to main content

React Hooks & Context

DEVELOPER Intermediate

The ez-console package re-exports UI building blocks from web/src/index.ts. This page documents the React hooks and context values you use most often inside pages and components rendered under EZApp (or any tree that includes the same providers).

For the full export catalog (components, api, client, types), see Package exports (index.ts).

For HTTP helpers (apiGet, apiPost, …) and the aggregated api object, see API integration. For declarative permission UI, see PermissionGuard / AdminGuard. For backend AI pipelines, see AI model integration.

useAuth

Reads authentication state from AuthProvider (mounted by EZApp).

Returns (shape from AuthContextType):

FieldTypeNotes
userAPI.User | null | undefinedundefined while the initial session check is in flight; null when logged out
loadingbooleanSession bootstrap loading
login(data: Partial<API.LoginRequest>) => Promise<API.User | null>Sets token and user on success; may throw for MFA / password-expired flows
oauthLogin(data: API.OAuthCallbackRequest) => Promise<API.User | null>OAuth callback completion
logout() => voidClears token and user
updateUser(user: API.User) => voidReplace the cached user object
errorError | undefinedLast error from loading the current user
import { useAuth } from 'ez-console';

export const ProfileSummary: React.FC = () => {
const { user, loading, logout } = useAuth();

if (loading || user === undefined) {
return null;
}

if (!user) {
return <span>Not signed in</span>;
}

return (
<span>
{user.username}
<button type="button" onClick={logout}>
Sign out
</button>
</span>
);
};

usePermission

RBAC checks for the current organization (useSite().currentOrgId). Roles are filtered to those that are global (organization_id unset) or belong to the active org. Users with the global admin role bypass permission checks.

Returns:

FieldTypeDescription
hasPermission(code: string) => booleanSingle permission code (e.g. backend-defined authorization:user:create)
hasAllPermissions(codes: string[]) => booleanEvery code must pass
hasAnyPermission(codes: string[]) => booleanAt least one code
hasGlobalPermission(code: string) => booleanLike hasPermission, but only roles without organization_id are considered
isAdminbooleanGlobal admin role present
loadingbooleantrue while user is still undefined
import { Button } from 'antd';
import { usePermission } from 'ez-console';

export const UserToolbar: React.FC = () => {
const { hasPermission, loading } = usePermission();

if (loading) {
return null;
}

return (
<>
{hasPermission('authorization:user:create') && (
<Button type="primary">New user</Button>
)}
</>
);
};

Use PermissionGuard when you prefer JSX over imperative checks. AdminGuard only renders children for global admins.

useSite

Site-wide configuration, current organization, and lightweight task notifications used by the shell.

Returns (highlights from SiteContextType):

FieldTypeDescription
siteConfigAPI.SiteConfig | nullFrom api.system.getSiteConfig() after login
loadingbooleanSite config request in flight
fetchSiteConfig() => Promise<API.SiteConfig | null>Manual refresh
enableMultiOrgbooleanDerived from siteConfig.enable_multi_org
enableSkillToolBindingbooleanDerived from siteConfig.enable_skill_tool_binding
currentOrgIdstring | nullPersisted under orgID in localStorage
setCurrentOrgId / clearCurrentOrgIdfunctionsSwitch or clear org context
tasks / setTasks / addTaskIn-app task list for long-running work
tasksDropdownOpen / setTasksDropdownOpenShell task dropdown visibility
import { useSite } from 'ez-console';

export const OrgBanner: React.FC = () => {
const { siteConfig, loading, currentOrgId, enableMultiOrg } = useSite();

if (loading || !enableMultiOrg) {
return null;
}

return <div>Active organization: {currentOrgId}</div>;
};

useAI

Global AI assistant UI state: layout, visibility, conversations list, and page-level tools / prompts for the embedded chat.

Typical uses:

  • Open the assistant with a prefilled user message: callAI(message, priorMessages?).
  • Register ephemeral system prompts, client tools, and optional page data for the current route (cleared automatically when the route changes inside AppLayout).

Exported types (for TypeScript): PageAIOptions, RegisteredClientTool, ClientToolHandler, PageDataGetter.

Context fields

FieldTypeDescription
layout'classic' | 'sidebar' | 'float-sidebar'Chat panel layout
setLayout(layout) => void
visiblebooleanChat open/closed
setVisible(visible: boolean) => void
callAI(message: string, messages?: API.SimpleChatMessage[]) => voidOpens chat and forwards to the registered handler
onCallAI(cb) => voidUsed internally by AIChat to receive callAI invocations
loaded / setLoadedboolean / setterChat surface mounted
fetchConversations() => Promise<API.AIChatSession[]>Loaded when chat becomes visible
fetchConversationsLoadingboolean
conversationsAPI.AIChatSession[] | undefined
activeConversationKey / setActiveConversationKeyPersisted as activeConversationKey in localStorage
ephemeralSystemPromptsstring[]Merged from the last registerPageAI call
clientToolsRegisteredClientTool[]Includes auto-registered ui_get_page_data when pageData is set
registerPageAI(opts: PageAIOptions) => () => voidRegister page context; returns an unregister function
resetPageAIContext() => voidClears prompts and tools

registerPageAI options (PageAIOptions)

OptionTypeDescription
ephemeralSystemPromptsstring[]Extra system instructions for this page only
toolsRegisteredClientTool[]Browser-side tools exposed to the model; each name must start with ui_
pageDataany | PageDataGetterStatic snapshot, object, or getter; when set, the framework adds tool ui_get_page_data
pageDataDescriptionstringAppended to the built-in tool description for the model
import { useEffect } from 'react';
import { useAI } from 'ez-console';

export const OrdersPage: React.FC = () => {
const { registerPageAI } = useAI();
const [orders, setOrders] = useState<API.Order[]>([]);

useEffect(() => {
const unregister = registerPageAI({
ephemeralSystemPrompts: [
'The user is on the Orders list. Prefer concise tables when summarizing.',
],
pageData: () => orders,
pageDataDescription: 'Current in-memory order rows for the active filters.',
tools: [
{
name: 'ui_focus_order',
description: 'Scroll the table to the order id given by the user.',
parameters: {
type: 'object',
properties: { orderId: { type: 'string' } },
required: ['orderId'],
},
handler: async (argsJson) => {
const { orderId } = JSON.parse(argsJson) as { orderId: string };
document.getElementById(`order-${orderId}`)?.scrollIntoView();
return JSON.stringify({ ok: true });
},
},
],
});

return unregister;
}, [registerPageAI, orders]);

return (/* ... */);
};

Invoke the assistant from your own UI:

const { callAI } = useAI();

callAI('Summarize the visible orders and highlight anomalies.');

For how models, toolsets, and SSE events work on the server, see AI model integration.

From the same package entry you will often import:

Need help? Ask in GitHub Discussions.