Server rendering
The default mode for sku is to statically render projects. However, Server-Side Rendering (SSR) can explicitly be turned on, both in development with hot module reloading for React, and in production.
First, you need to create a sku.config.js file, which will contain the following setup at minimum:
export default {
clientEntry: 'src/client.js',
serverEntry: 'src/server/server.js',
public: 'src/public',
publicPath: '/',
target: 'dist',
port: 3300,
serverPort: 3301,
} satisfies SkuConfig;If you have an existing configuration, for example generated with @sku-lib/create, you will need to replace the render entry point by a server entry point, and add port info as documented above.
Then, you need to create your server entry. Sku will automatically provide an Express server for the user. The entry point for SSR, server, is used to provide the following:
- a render callback
- optionally, any required middlewares, either one or an array
- optionally, a callback to run after the server starts, which receives the Express application instance as a parameter
This can be done as follows:
import template from './template';
import middleware from './middleware';
import type { Server } from 'sku';
export default (): Server => ({
renderCallback: ({ SkuProvider, getBodyTags, getHeadTags }, req, res) => {
const app = renderToString(
<SkuProvider>
<App />
</SkuProvider>,
);
res.send(
template({ headTags: getHeadTags(), bodyTags: getBodyTags(), app }),
);
},
middleware: middleware,
onStart: (app) => {
console.log('My app started 👯♀️!');
app.keepAliveTimeout = 20000;
},
});Multi-part response
If you need to return HTML at different times in the request you can use flushHeadTags to retrieve only the new head tags since the previous call.
New head tags can be added during render, typically this is due to dynamic chunks being used during a render.
For example, you may want to send back an initial response before you are done rendering your response:
import { initialResponseTemplate, followupResponseTemplate } from './template';
import middleware from './middleware';
import type { Server } from 'sku';
export default (): Server => ({
renderCallback: ({ SkuProvider, getBodyTags, getHeadTags }, req, res) => {
res.status(200);
// Call `flushHeadTags` early to retrieve whatever tags are available.
res.write(initialResponseTemplate({ headTags: flushHeadTags() }));
await Promise.resolve();
const app = renderToString(
<SkuProvider>
<App />
</SkuProvider>,
);
res.write(
// Call `flushHeadTags` again just in case new tags are available.
followupResponseTemplate({
headTags: flushHeadTags(),
bodyTags: getBodyTags(),
app,
}),
);
res.end();
},
middleware: middleware,
onStart: (app) => {
console.log('My app started 👯♀️!');
app.keepAliveTimeout = 20000;
},
});Last but not least, please note that commands for SSR are different to the ones used normally:
- Use
sku start-ssrto start your development environment. It uses bothportandserverPortto spin up hot module reloading servers. - Use
sku build-ssrto build your production assets. You can then runnode ./dist/server.js. Your server will run athttp://localhost:xxxx, wherexxxxisserverPort. - Use
sku testto test your application
Multi-language support
When using multiple languages the browser will download the language needed as required. However, this can lead to a delay in page load. To ensure translations are available immediately you need to tell sku what language you are rendering.
Note: This is handled automatically for static-rendering and is only required for server-side rendering.
To add the language to the initial render call addLanguageChunk from your render params.
Example: Using addLanguageChunk to set the language during server-render
export async function serverRender({ SkuProvider, addLanguageChunk, appPath }) {
const language = getLanguageFromPath(appPath);
addLanguageChunk(language);
return renderToString(
<SkuProvider>
<StaticRouter location={appPath}>
<VocabProvider language={language}>
<App />
</VocabProvider>
</StaticRouter>
</SkuProvider>,
);
}Development server entrypoint
When developing your application sku will start two services:
- A dev server responsible for serving static assets
- An SSR service running your app's server code
The dev server acts as a single entrypoint for your development environment, proxying requests to your SSR service that don't match any other known routes. This simulates a typical production environment, where a reverse proxy directs asset, API or other requests to another service. It also avoids the need to complete Cross-Origin Resource Sharing (CORS) checks when making requests from the client.
To include other requests, like typical API traffic, consider using Dev Server Middleware to proxy requests.
