Create a dynamic component
For a component to have dynamic behavior -that is, to being able to update or re-render its contents without reloading the whole page- it needs to be explicitely configured as a dynamic component.
Currently, creating a dynamic process is a bit more convolved process than creating a static component, and it requires the usage of the GetDynamicComponent helper function:
export const MyDynamicComponent = GetDynamicComponent<MyData,SXLGlobalContext>(
"my-dynamic-component",
async () => {
const data = await getData()
return data;
},
(data, props) => {
if (data.isPending) {
return <>Loading...</>;
}
return <div>Loaded: {data.value}</div>
},
);
// ...
<MyDynamicComponent.Render />
The GetDynamicComponent function receives three arguments:
- Component ID (
string): A unique ID used to identify the dynamic component. - Data Fetcher (
() => MyData): A function that returns aPromisewith the data the component needs to fetch. The type for the Promise contents is passed in theGetDynamicComponentfirst type parameter (e.g.MyData). - Render function: (
(data:TrackedPromise<MyData>, props: SXL.Props) => JSX.Element): A function that receives the promise returned by the data fetcher, wrapped inTrackedPromise-which is an extension forPromisethat exposes methods for tracking its state-, and the props passed to the component.
Updating a dynamic component
All dynamic components can be updated using the webAction helper:
<button
onclick={webAction({}, (ev, webContext) => {
webContext?
.actions?
.refetchElement("my-dynamic-component", {
// pass query parameters
});
})}>
Reload
</button>
This helper creates all bindings necessary to update a dynamic component.
The refetchElement receives two parameters:
- Component ID (
string): The ID for the component to update - Query parameters (
Record<string, string>): A map of query parameters for the updated component.
Example: Rendering the server's date
Let us create an example to understand how to update dynamic components.
First, we will create a dynamic component called ServerDateComponent:
export const ServerDateComponent = GetDynamicComponent<
Date,
SXLGlobalContext & { mmDDYY?: boolean }
>(
"my-server-date-component",
async () => {
const serverDate = await getServerDate();
return serverDate;
},
(data, props) => {
if (data.isPending) {
return <>Loading...</>;
}
const serverDate: Date = data.value;
if (props?.globalContext?.mmDDYY) {
const dateFormatted = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "2-digit",
day: "2-digit",
}).format(serverDate);
return <div>Server date: {dateFormatted}</div>;
}
return <div>Server date: {serverDate.toISOString()}</div>;
},
);
// ...
<ServerDateComponent.Render />
The ServerDateComponent does a couple of things:
- Two type parameters are passed to the
GetDynamicComponentsignature:Date: The type for the data returned by the fetcher.SXLGlobalContext & { mmDDYY?: boolean }: An extension of the global contextSXLGlobalContextwith the boolean query parametermmDDYY.
- We pass a fetcher function that retrieves a date from the server using
getServerDate(). - While the component is fetching the server date, a
Loadingmessage will be displayed. - Once the promise is resolved, the server
Dateobject is retrieved usingdata.value. - Then, depending whether
mmDDYYistrueorfalse, it renders a string representation either in formatmm/dd/yyor ISO.
Note: All this logic happens on the server side. The dynamic component on the browser-side will receive the fully rendered HTML content returned by the component. Any temporary state contained by the component on the browser (e.g. the text in any input field) will be discarded if it's not explicitely sent to the server as a query parameter.
Now, let's create a second static component which will update the contents of our dynamic ServerDateComponent:
export function ReplacerComponent() {
return (
<>
<button
onclick={webAction({}, (ev, webContext) => {
webContext?.actions?.refetchElement(
"my-server-date-component",
{
mmDDYY: true,
},
);
})}
>
Get server date on mm/dd/yyyy format
</button>
<button
onclick={webAction({}, (ev, webContext) => {
console.log("Replace");
webContext?.actions?.refetchElement(
"my-server-date-component",
{},
);
})}
>
Get server date on ISO format
</button>
</>
);
}
// ...
<ReplacerComponent/>
Notice a few things here:
- Static components can use the
webActionhelper. A component doesn't need to be a dynamic component to update another dynamic component. - The component renders two buttons, both of which updates the dynamic component using its ID (
"my-server-date-component"):- The first button passes the
mmDDYYquery parameter set totrue. - The second button passes no query parameters, which will re-render the dynamic component using its default state (rendering the date in ISO format).
- The first button passes the
After rendering both components, we can see the dynamic component being updated correctly:

Configuring query params in the middlware
Currently, query parameters like mmDDYY need to be configured in LeanJSX middleware to be parsed from the request query parameters if they are not part of the global context:
app.use(
LeanApp.middleware({
configResponse: (resp) => resp.set("Content-Security-Policy", CSP),
globalContextParser: (req, componentId) => {
if (componentId === ServerDateComponent.contentId) {
return {
...parseQueryParams(req),
...{
mmDDYY: Boolean(req.query?.mmDDYY),
},
};
}
return parseQueryParams(req);
},
}),
);
We add a check in globalContextParser to see if componentId matches our component's ID, and if it does, we parse the mmDDYY query param. This allows us have query params that are specific to a component without polluting the global context.
Note: The middleware configuration may not be needed in future versions of LeanJSX.