--- myst: html_meta: "description": "Integrate with Volto’s asyncConnect for SSR" "property=og:description": "Integrate with Volto’s asyncConnect for SSR" "property=og:title": "Integrate with Volto’s asyncConnect for SSR" "keywords": "Volto, Plone, SSR, SPA" --- # Integrate with Volto’s asyncConnect for SSR We already know that Volto provides full server-side rendering of the React components, making it an isomorphic application. How does that work? In simplified pseudocode, it works like this: - in server.jsx we have code like `react-dom.renderToString()` - the Router renders its declared components, which is the `App` and its direct child, the `View` component Now, here is where it gets tricky: the View component should have the content from the backend for the current URL, but it that content is fetched via an async backend endpoint call. So we need a mechanism to "stop" the processing of the renderToString and make it wait until the backend content has arrived. In Volto this is solved with the `asyncConnect()` HOC helper, which is a port of [redux-connect][1] The internal implementation uses Redux and the "trick" is to prepopulate the Redux store with the information that would be needed in your components: Here's an example where the `asyncPropsExtenders` configuration is used to prefetch a `footer-links` page from the backend and include it with every SSR: ```js config.settings.asyncPropsExtenders = [ ...(config.settings.asyncPropsExtenders || []), { path: '/', extend: (dispatchActions) => { const action = { key: 'footer', promise: ({ location, store }) => { // const currentLang = state.intl.locale; const bits = location.pathname.split('/'); const currentLang = bits.length >= 2 ? bits[1] || DEFAULT_LANG : DEFAULT_LANG; const state = store.getState(); if (state.content.subrequests?.[`footer-${currentLang}`]?.data) { return; } const url = `/${currentLang}/footer-links`; const action = getContent(url, null, `footer-${currentLang}`); return store.dispatch(action).catch((e) => { // eslint-disable-next-line console.log( `Footer links folder not found: ${url}. Please create as folder named footer-links in the root of your current language`, ); }); }, }; return [...dispatchActions, action]; }, }, ]; ``` Note: this example is a low-tech "frontend-er only" solution. In real life you will probably want to devise a mechanism where that footer-links information is automatically included with every content request. Notice the extender mechanism, we register a "modifier" for the current list of "async connect dispatch actions". ``` config.settings.asyncPropsExtenders = [ ...config.settings.asyncPropsExtenders, { path: '/', extend: (dispatchActions) => dispatchActions.filter(asyncAction=> asyncAction.key !== 'breadcrumb') } ] ``` [1]: https://github.com/makeomatic/redux-connect/