code-docs/packages/client.md
React client for rendering Lowdefy pages. Orchestrates block rendering, context management, and user interactions.
This package provides:
Client React component that renders pagesimport Client from '@lowdefy/client';
// Used by the server to render pages
<Client
auth={authSession}
Components={componentMap}
config={pageConfig}
jsMap={customJsFunctions}
lowdefy={lowdefyContext}
router={nextRouter}
stage={buildStage}
types={typeDefinitions}
window={windowObject}
/>;
┌─────────────────────────────────────────────────────────────────┐
│ Client │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ProgressBarCtrl │ │ DisplayMessage │ │ Context │ │
│ │ (loading state) │ │ (notifications) │ │ (page state) │ │
│ └─────────────────┘ └─────────────────┘ └────────┬────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Head │ │
│ │ (meta tags) │ │
│ └───────────────┘ │
│ ┌───────▼───────┐ │
│ │ Block │ │
│ │ (root block) │ │
│ └───────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Module | Purpose |
|---|---|
Client.js | Main entry component, initializes lowdefy context |
Context.js | Page context provider, manages state and requests |
Head.js | HTML head management (title, meta) |
DisplayMessage.js | Toast notifications and messages |
ProgressBarController.js | Loading progress indicator |
useDarkMode.js | Dark mode hook (config → localStorage → OS pref) |
/block/)| Module | Purpose |
|---|---|
Block.js | Renders individual blocks with their components |
| (in engine) | Block state, events, and lifecycle |
| Module | Purpose |
|---|---|
createCallRequest.js | Creates function to call data requests |
createCallAPI.js | Creates function to call custom endpoints |
request.js | HTTP request utilities |
| Module | Purpose |
|---|---|
createShortcutManager.js | Global keyboard shortcut listener lifecycle (tinykeys) |
createShortcutBadge.js | ShortcutBadge React component for visual key indicators |
ShortcutManager lifecycle: Initialized on page context creation → walks block tree to collect all shortcuts → registers a single global keydown listener via tinykeys → checks block visibility lazily per handler → destroyed on context change/unmount.
ShortcutBadge is registered in initLowdefyContext.js as a component (alongside Icon and Link). Blocks receive it via props and render it next to titles/labels. It detects the platform (Mac vs Windows/Linux) and renders modifier symbols accordingly (⌘/⇧/⌥ on Mac, Ctrl/Shift/Alt elsewhere).
See keyboard-shortcuts.md for the full data flow.
| Module | Purpose |
|---|---|
initLowdefyContext.js | Sets up the global lowdefy context |
createHandleError.js | Creates error handler with dedup, server round-trip, display |
setupLink.js | Configures navigation links |
createLinkComponent.js | Creates the Link component for navigation |
createIcon.js | Creates icon rendering function |
/auth/)Handles auth state and session management on the client.
The lowdefy object is the central context passed through the app:
lowdefy = {
// User session
user: { id, email, roles, ... },
// Navigation
router: nextRouter,
Link: LinkComponent,
// Configuration
home: { pageId, configured },
menus: [...],
urlQuery: { ... },
// Internal
_internal: {
blockComponents: { ... }, // Loaded block components
displayMessage: fn, // Show toast messages
callRequest: fn, // Call data requests
callEndpoint: fn, // Call API endpoints
handleError: fn, // Error handler with dedup, server round-trip, display
logger: browserLogger, // Shared browser logger (createBrowserLogger)
...
}
}
Each page has its own context (from @lowdefy/engine):
context = {
// State containers
state: { ... }, // Form/input values
requests: { ... }, // Request responses
// Internals
_internal: {
RootAreas: { ... }, // Block tree
onInitDone: boolean, // Initialization complete
...
}
}
1. Client receives props from server
│
▼
2. initLowdefyContext()
- Set up auth, router, components
- Initialize display message handler
│
▼
3. Render ProgressBarController
- Shows loading state during transitions
│
▼
4. Render DisplayMessage
- Toast notification container
│
▼
5. Render Context (page context provider)
- Creates page context via @lowdefy/engine
- Handles onInit events
- Manages state
│
▼
6. Wait for onInitDone
│
▼
7. Render Head (meta tags)
│
▼
8. Render root Block
- Recursively renders block tree
- Each block gets its component and props
Client handles React-specific concerns:
Engine handles framework-agnostic logic:
This separation allows potential non-React implementations.
Each page gets isolated context because:
The lowdefy object provides:
Blocks receive it as a prop, not via React context, for performance.
classNames.block/styles.block to BlockLayout, and layout components accept className/style props instead of the deprecated makeCssClass/blockStyle/areaStyle pattern.User clicks button
│
▼
Block fires onClick event
│
▼
@lowdefy/engine executes actions
│
├──► SetState action
│ Updates context.state
│ Triggers re-render
│
├──► Request action
│ Calls createCallRequest()
│ HTTP to /api/request
│ Response stored in state
│
└──► Navigate action
Calls lowdefy.router.push()
Client unmounts, new page loads