docs/react-wiki-archive/Server-side-rendering-and-browserless-testing.md
For basic instructions on getting Next.js set up, see https://nextjs.org/
pages folder, as guided by the tutorial.@fluentui/reactyarn add @fluentui/react
_document.js file under your pages folder with the following content:import * as React from 'react';
import Document, { Head, Html, Main, NextScript } from 'next/document';
import { Stylesheet, resetIds } from '@fluentui/react';
// Fluent UI React (Fabric) 7 or earlier
// import { Stylesheet, resetIds } from 'office-ui-fabric-react';
const stylesheet = Stylesheet.getInstance();
// Now set up the document, and just reset the stylesheet.
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
resetIds();
const page = renderPage(App => props => <App {...props} />);
return { ...page, styleTags: stylesheet.getRules(true), serializedStylesheet: stylesheet.serialize() };
}
render() {
return (
<Html>
<Head>
<style type="text/css" dangerouslySetInnerHTML={{ __html: this.props.styleTags }} />
<!--
This is one example on how to pass the data.
The main purpose is to set the config before the Stylesheet gets initialised on the client.
Use whatever method works best for your setup to achieve that.
-->
<script type="text/javascript" dangerouslySetInnerHTML={{ __html: `
window.FabricConfig = window.FabricConfig || {};
window.FabricConfig.serializedStylesheet = ${this.props.serializedStylesheet};
` }} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
import * as React from 'react';
import {
Checkbox,
ColorPicker,
createTheme,
Dropdown,
ThemeProvider, // NOTE: Use Fabric instead in version 7 or earlier
initializeIcons,
PrimaryButton,
Slider,
TextField,
Toggle,
} from '@fluentui/react';
initializeIcons();
const Index = () => (
<ThemeProvider>
<div>
<PrimaryButton>Hello, world</PrimaryButton>
<Toggle defaultChecked label="Hello" />
<TextField defaultValue="hello" />
<Dropdown disabled />
<Checkbox defaultChecked label="Hello" />
<Slider defaultValue={50} max={100} />
<ColorPicker />
</div>
</ThemeProvider>
);
export default Index;
Note: There are many steps missing below to get nodemon/babel/typescript/es modules working in a node.js environment. This will need to be elaborated on.
It's possible to render Fluent UI React components on the server side in a Node environment, using the SSR support in merge-styles.
See https://codesandbox.io/s/dazzling-montalcini-kv9bz for an example which uses the SSR support to build html/css strings to inject into the page. The result is not mounted, so behaviors will not work, but represents the html/css output that would be generated by SSR.
Example:
import * as React from 'react';
import * as ReactDOM from 'react-dom/server';
import {
ThemeProvider, // NOTE: Use Fabric instead in version 7 or earlier
// ...
} from '@fluentui/react';
import { renderStatic } from '@fluentui/merge-styles/lib/server';
// Fluent UI React (Fabric) 7 or earlier
// import { renderStatic } from '@uifabric/merge-styles/lib/server';
import './styles.css';
initializeIcons();
function App() {
return <ThemeProvider>...content goes here...</ThemeProvider>;
}
const serverRenderExample = () => {
const { html, css } = renderStatic(() => ReactDOM.renderToString(<App />));
// Use the html and css string content to inject into the response
};
serverRenderExample();
In unit or end-to-end tests that run in an SSR-like (non-browser) environment such as Node, you'll need to disable style loading.
const { initializeIcons, setRTL, setResponsiveMode, ResponsiveMode } = require('@fluentui/react');
const themeLoader = require('@microsoft/load-themed-styles');
initializeIcons('dist/');
// Configure load-themed-styles to avoid registering styles.
themeLoader.configureLoadStyles(styles => {
// noop
});
// Set rtl to false.
setRTL(false);
// Assume a large screen.
setResponsiveMode(ResponsiveMode.large);
You'll also want to mock out requiring .scss files. In Jest:
moduleNameMapper: {
// jest-style-mock.js should just contain module.exports = {};
'\\.(scss)$': path.resolve(__dirname, 'jest-style-mock.js'),
}
Some of our legacy styling was done through scss rather than merge-styles. Keeping legacy info here in case there are still scenarios which need to pipe load-themed-styles based styling into a server response.
The basic idea is to tell the styles loader to store styles in a variable, which you can later inject into your page. Example:
import { configureLoadStyles } from '@microsoft/load-themed-styles';
// Store registered styles in a variable used later for injection.
let _allStyles = '';
// Push styles into variables for injecting later.
configureLoadStyles((styles: string) => {
_allStyles += styles;
});
import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { Text } from '@fluentui/react';
let body = ReactDOMServer.renderToString(<Text>hello</Text>);
console.log(
`
<html>
<head>
<style>${_allStyles}</style>
</head>
<body>
${body}
</body>
</html>
`,
);