@@ -190,11 +190,52 @@ In case or React rendering you may use **interleaved streaming**, which would no
190
190
similar how StyledComponents works
191
191
192
192
` ` ` js
193
- import {discoverProjectStyles , createLink , createStyleStream } from ' used-styles' ;
194
- import MultiStream from ' multistream' ;
193
+ import express from ' express' ;
194
+ import {
195
+ discoverProjectStyles ,
196
+ loadStyleDefinitions ,
197
+ createCriticalStyleStream ,
198
+ createStyleStream ,
199
+ createLink ,
200
+ } from ' used-styles' ;
201
+
202
+ const app = express ();
195
203
196
204
// generate lookup table on server start
197
- const stylesLookup = discoverProjectStyles (' ./build' ); // __dirname usually
205
+ const stylesLookup = isProduction
206
+ ? discoverProjectStyles (' ./dist/client' )
207
+ // load styles for development
208
+ : loadStyleDefinitions (async () => []);
209
+
210
+ app .use (' *' , async (req , res ) => {
211
+ await stylesLookup;
212
+
213
+ try {
214
+ const renderApp = (await import (' ./dist/server/entry-server.js' )).default ;
215
+
216
+ // create a style steam
217
+ const styledStream = createStyleStream (stylesLookup, (style ) => {
218
+ // _return_ link tag, and it will be appended to the stream output
219
+ return createLink (` dist/${ style} ` ) // <link href="dist/mystyle.css />
220
+ });
221
+
222
+ // or create critical CSS stream - it will inline all styles
223
+ const styledStream = createCriticalStyleStream (stylesLookup); // <style>.myClass {...
224
+
225
+ await renderApp ({ res, styledStream });
226
+ } catch (err) {
227
+ res .sendStatus (500 );
228
+ }
229
+ });
230
+ ` ` `
231
+
232
+ ` ` ` js
233
+ // entry-server.js
234
+ import React from ' react' ;
235
+ import { renderToPipeableStream } from ' react-dom/server' ;
236
+ import MultiStream from ' multistream' ;
237
+ import { Readable } from ' node:stream' ;
238
+ import App from ' ./App' ;
198
239
199
240
// small utility for "readable" streams
200
241
const readableString = string => {
@@ -205,37 +246,52 @@ const readableString = string => {
205
246
return s;
206
247
};
207
248
208
- async function MyRender () {
209
- // render App
210
- const htmlStream = ReactDOM .renderToNodeStream (< App/ > )
211
-
212
- await stylesLookup;
213
- // create a style steam
214
- const styledStream = createStyleStream (stylesLookup, (style ) => {
215
- // _return_ link tag, and it will be appended to the stream output
216
- return createLink (` dist/${ style} ` ) // <link href="dist/mystyle.css />
217
- });
218
-
219
- // or create critical CSS stream - it will inline all styles
220
- const styledStream = createCriticalStyleStream (stylesLookup); // <style>.myClass {...
221
-
222
- // allow client to start loading js bundle
223
- res .write (` <!DOCTYPE html><html><head><script defer src="client.js"></script>` );
224
-
225
- const middleStream = readableString (' </head><body><div id="root">' );
226
- const endStream = readableString (' </head><body>' );
227
-
228
- // concatenate all steams together
229
- const streams = [
230
- middleStream, // end of a header, and start of a body
231
- styledStream, // the main content
232
- endStream, // closing tags
233
- ];
234
-
235
- MultiStream (streams).pipe (res);
236
-
237
- // start by piping react and styled transform stream
238
- htmlStream .pipe (styledStream);
249
+ const ABORT_DELAY = 10000 ;
250
+
251
+ async function renderApp ({ res, styledStream }) {
252
+ let didError = false ;
253
+
254
+ const { pipe , abort } = renderToPipeableStream (
255
+ < React .StrictMode >
256
+ < App / >
257
+ < / React .StrictMode > ,
258
+ {
259
+ onShellError () {
260
+ res .sendStatus (500 );
261
+ },
262
+ onAllReady () {
263
+ res .status (didError ? 500 : 200 );
264
+ res .set ({ ' Content-Type' : ' text/html' });
265
+
266
+ // allow client to start loading js bundle
267
+ res .write (` <!DOCTYPE html><html><head><script defer src="client.js"></script></head><body><div id="root">` );
268
+
269
+ const endStream = readableString (' </div></body></html>' );
270
+
271
+ // concatenate all streams together
272
+ const streams = [
273
+ styledStream, // the main content
274
+ endStream, // closing tags
275
+ ];
276
+
277
+ new MultiStream (streams).pipe (res);
278
+
279
+ // start by piping react and styled transform stream
280
+ pipe (styledStream);
281
+ },
282
+ onError (error ) {
283
+ didError = true ;
284
+ console .error (error);
285
+ }
286
+ },
287
+ );
288
+
289
+ setTimeout (() => {
290
+ abort ();
291
+ }, ABORT_DELAY );
292
+ }
293
+
294
+ export default renderApp ;
239
295
` ` `
240
296
241
297
**!! THIS IS NOT THE END !!** Interleaving links and react output would break a client side rehydration, as long as _
@@ -244,9 +300,20 @@ injected_ links were not rendered by React, and not expected to present in the "
244
300
You have to move injected styles out prior rehydration.
245
301
246
302
` ` ` js
303
+ import React from ' react' ;
247
304
import { moveStyles } from ' used-styles/moveStyles' ;
305
+ import ReactDOM from ' react-dom/client' ;
306
+ import App from ' ./App' ;
307
+
308
+ // Call before `ReactDOM.hydrateRoot`
309
+ moveStyles ()
248
310
249
- moveStyles ();
311
+ ReactDOM .hydrateRoot (
312
+ document .getElementById (' root' ),
313
+ < React .StrictMode >
314
+ < App / >
315
+ < / React .StrictMode >
316
+ );
250
317
` ` `
251
318
252
319
You might want to remove styles after rehydration to prevent duplication. Double check that corresponding _real_ CSS is
0 commit comments