Isomorphic Pages and Components
In web development, there is a concept of isomorphism, which means that the same code can run both on the server and on the client side in the browser. This allows developers to create applications that work equally efficiently in both environments using the same technologies and development tools.
The advantage of isomorphism is that developers do not need to write separate code for the server-side and client-side parts of the application. They can use the same code that adapts to the execution environment.
Components that will be used in the browser (isomorphic components) should be marked in the file with a comment on the first line: // @shared
.
If a component is marked this way, its code can be sent to the browser. If there is no mark, the code is not sent to the browser.
// @shared
export function MyComponent(props: MyComponentProps) {
const ref = nanoid() // component identifier
return (
<div>
<MyComponentInitializer
ctx={ctx} // context
ref={ref} // identifier
title={'one'} // component properties
subTitle={'two'}
/>
</div>
)
}
export const MyComponentInitializer = createClientInitializer(async ({ ctx, element, ...props }) => {
// element = <div>
// props.title
// props.subtitle
console.log('This code will only run on the client')
})
In one file, both the body of the component that will run on the server and the client initialization code are defined.
Since components have a special initializer (createClientInitializer
), special initialization is required for their correct operation when dynamically adding or removing them on the client. For this, there are methods similar to DOM methods for inserting tree elements, which allow components to initialize correctly.
Auxiliary Attributes style
and script
For style
and script
tags, additional attributes portal
and portalDedupe
can be used.
portal
can take values: body-start
, body-end
, head-start
, head-end
. It indicates which tag to move the loading of the script or styles to.
portalDedupe
means that if such a script or style has already been added, it does not need to be added again.
Example usage:
<link
href="/my-component.css"
rel="stylesheet"
portal="head-end"
portalDedupe
/>
Client-Side Methods
appendChild
The component is added as a child as the last element. This is used when creating a new element on the client.
await appendChild(
element,
<MyComponent
ctx={ctx}
title={'Second Title'}
subTitle={'Second Subtitle'}
/>
)
insertBefore
The component is added as a child before the specified element. This is used when creating a new element on the client and displaying it in a specific place among siblings.
await insertBefore(
element,
<MyComponent
ctx={ctx}
title={'Will be First'}
subTitle={'Subtitle'}
/>,
firstElement
)
replaceWith
The component replaces an element. It can be used when changing a component or for re-rendering the entire component.
let componentElement = await replaceWith(
componentElement,
<MyComponent
ctx={ctx}
title={'Updated Title'}
subTitle={'Updated Subtitle'}
/>
)
toHtmlElement
There is a helper function that prepares the component and returns it as a DOM element for further actions using the standard browser SDK.
It takes document
as the first argument.
const myComponentElement = await toHtmlElement(
document,
<MyComponent
ctx={ctx}
title={'Updated Title'}
subTitle={'Updated Subtitle'}
/>
)
document.body.appendChild(myComponentElement)