Server-Side Rendering Support
Content blocks can optionally provide server-side rendering (SSR) support to preload data during the server rendering process. This ensures that the necessary data is available when the component is rendered on the server, improving initial page load performance and SEO.
preloadDataForServerRendering Method
The ContentBlock<M> component can optionally provide a preloadDataForServerRendering method with the following signature:
preloadDataForServerRendering?: (options: ServerRenderingPreloadOptions<M>) => void;
This method is invoked during the server-side rendering process to allow the content block to request the data required for its rendering.
ServerRenderingPreloadOptions Type
The ServerRenderingPreloadOptions<M> type contains the following properties:
| Property | Type | Description |
|---|---|---|
| store | { getState(): DefaultRootState; dispatch(action: Action): void } | The add-on store with methods to get state and dispatch actions. |
| model | M | The content block model. |
| context | AppContext | Current application context data available for add-ons. |
| page | ServerRenderingProductPage | ServerRenderingProductGroupPage | ServerRenderingProductListPage | ServerRenderingSearchPage | null | The current page information. |
Store Object
The store object provides access to:
getState()- Gets the current state from the storedispatch(action)- Dispatches an action to the store
AppContext Type
For detailed information about the AppContext type and its properties, see App Context.
Page Types
The page property can be one of the following types or null:
ServerRenderingProductPage
type ServerRenderingProductPage = {
/** The page product. */
product: Product | CalculatedProduct;
};
For more information about the Product and CalculatedProduct types, see useProductContext hook.
ServerRenderingProductGroupPage
type ServerRenderingProductGroupPage =
& NonNullable<ReturnType<typeof useProductGroupContext>>
& Omit<NonNullable<ReturnType<typeof useProductGroupFilters>>, 'applyFiltersSelection'>;
For more information about the useProductGroupContext, see useProductGroupContext hook. For more information about the useProductGroupFilters, see useProductGroupFilters hook.
ServerRenderingProductListPage
type ServerRenderingProductListPage = NonNullable<ReturnType<typeof useProductListPage>> & {
/** The list of products available on the page. */
products: Array<Product | ProductGroup>;
};
For more information about the useProductListPage, see useProductListPage hook. For more information about the Product and CalculatedProduct types, see useProductLine hook.
ServerRenderingSearchPage
type ServerRenderingSearchPage = NonNullable<ReturnType<typeof useSearchPage>> & {
/** The list of products available on the page. */
products: Array<Product | ProductGroup>;
};
For more information about the useSearchPage, see useSearchPage hook. For more information about the Product and CalculatedProduct types, see useProductLine hook.
Example with Server-Side Rendering
import type { ContentBlock } from 'sana/types';
import { memo, useEffect, useRef } from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import { requestProductSet } from 'behavior';
import ProductTiles from './ProductTiles';
import { useHasAbilities, parseSortOption } from 'sana/utils';
type Model = {
maximumProductsToShow: number;
productSetId: string;
sortOption: string;
};
const ProductSetBlock: ContentBlock<Model> = ({ model, id }) => {
const [canViewCatalog] = useHasAbilities('VIEW_CATALOG');
const dispatch = useDispatch();
const productsKey = createProductsKey(model);
const productSet = useSelector(s => s.productSets && s.productSets[productsKey]);
const modelRef = useRef<Model>();
const modelExpired = modelRef.current && !shallowEqual(modelRef.current, model);
modelRef.current = model;
useEffect(() => {
if (!canViewCatalog)
return;
if (!productSet || productSet.expired || modelExpired)
dispatch(createRequestProductsAction(productsKey, modelRef.current!));
});
const products = productSet?.products;
if (!products?.length)
return null;
return <ProductTiles products={products} id={id} />;
};
const ProductSetBlockMemo: ContentBlock<Model> = memo(ProductSetBlock);
// Server-side rendering support
ProductSetBlockMemo.preloadDataForServerRendering = ({ model, store, context, page }) => {
// Check if user has permission to view catalog using context
if (!context.user.canViewCatalog)
return;
const productsKey = createProductsKey(model);
// Dispatch action to preload product data during SSR
store.dispatch(createRequestProductsAction(productsKey, model));
};
export default ProductSetBlockMemo;
function createRequestProductsAction(productsKey: string, model: Model) {
const { maximumProductsToShow, productSetId, sortOption } = model;
const sorting = sortOption ? [parseSortOption(sortOption)] : null;
return requestProductSet(productsKey, productSetId, maximumProductsToShow, sorting);
}
function createProductsKey(model: Model) {
return `${model.productSetId}_${model.maximumProductsToShow}_${model.sortOption}`;
}
Implementation Notes
In this example:
- The content block renders a set of products using the
ProductTilescomponent - The
ProductTilescomponent,requestProductSetaction and add-on redux store implementations are out of scope for this example - The example uses the useHasAbilities hook to check user permissions
- The
preloadDataForServerRenderingmethod checks user permissions viacontext.user.canViewCatalog - It dispatches a Redux action to preload product data during server-side rendering
- The same logic is used both in the component's
useEffectand the SSR preload method - The method ensures that product data is available when the page is rendered on the server