Style loading utilities that provide CSS-in-JS functionality with automatic vendor prefixing, RTL support, and server-side rendering capabilities.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Utilities for rendering styles on the server and extracting CSS rules for client hydration. This enables merge-styles to work seamlessly in server-side rendered applications by capturing generated CSS rules during render.
Important: Server-side rendering utilities are available from a separate import path: @uifabric/merge-styles/lib/server, not from the main package export.
Renders content and extracts CSS rules generated during the render process. Essential for server-side rendering where you need to capture dynamically generated styles.
/**
* Renders content and returns both HTML and CSS needed for the HTML
* @param onRender - Function that returns the HTML string to render
* @param namespace - Optional namespace to prepend to CSS class names to avoid collisions
* @returns Object containing html string and css string
*/
function renderStatic(onRender: () => string, namespace?: string): { html: string; css: string };Usage Examples:
React Server-Side Rendering:
import { renderStatic } from '@uifabric/merge-styles/lib/server';
import { renderToString } from 'react-dom/server';
import { MyApp } from './MyApp';
// Server-side render with style extraction
const { html, css } = renderStatic(() => {
return renderToString(<MyApp />);
});
// Send HTML with extracted CSS
const fullHtml = `
<!DOCTYPE html>
<html>
<head>
<style>${css}</style>
</head>
<body>
<div id="root">${html}</div>
</body>
</html>
`;With Namespace for Multi-App Scenarios:
// Prevent CSS class name collisions
const { html, css } = renderStatic(() => {
return renderToString(<MyApp />);
}, 'myapp'); // Classes will be prefixed with 'myapp-'
// CSS classes become: .myapp-css-0, .myapp-root-1, etc.Next.js Integration:
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { renderStatic } from '@uifabric/merge-styles/lib/server';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => {
const { html, css } = renderStatic(() => <App {...props} />);
return { html, css };
},
});
const initialProps = await Document.getInitialProps(ctx);
return initialProps;
}
render() {
return (
<Html>
<Head>
<style dangerouslySetInnerHTML={{ __html: this.props.css }} />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;Rule Registration Timing:
// ❌ Problematic: Rules registered at file scope won't be captured
const rootClass = mergeStyles({ background: 'red' });
const App = () => <div className={rootClass} />;
// This won't capture the rootClass styles
renderStatic(() => ReactDOM.renderToString(<App />));
// ✅ Better: Register styles during render
const App = () => {
const rootClass = mergeStyles({ background: 'red' });
return <div className={rootClass} />;
};
// This will capture all styles
renderStatic(() => ReactDOM.renderToString(<App />));Performance Optimization with Memoization:
import { memoizeFunction } from '@uifabric/utilities';
// Memoize style functions to reduce recalculation overhead
const getStyles = memoizeFunction((theme: ITheme) => {
return mergeStyleSets({
root: {
background: theme.palette.neutralLight,
padding: '20px'
},
title: {
color: theme.palette.neutralPrimary,
fontSize: theme.fonts.large.fontSize
}
});
});
const MyComponent = ({ theme }) => {
const styles = getStyles(theme);
return (
<div className={styles.root}>
<h1 className={styles.title}>Title</h1>
</div>
);
};Client-Side Hydration:
// Client-side hydration setup
import { hydrate } from 'react-dom';
import { MyApp } from './MyApp';
// The client will reuse server-generated CSS classes
// and generate new ones as needed
hydrate(<MyApp />, document.getElementById('root'));When running multiple applications or versions on the same page, use namespaces to prevent CSS class name collisions.
// App 1
const { html: html1, css: css1 } = renderStatic(() => {
return renderToString(<App1 />);
}, 'app1');
// App 2
const { html: html2, css: css2 } = renderStatic(() => {
return renderToString(<App2 />);
}, 'app2');
// Combined HTML with isolated CSS
const combinedHtml = `
<style>${css1}</style>
<style>${css2}</style>
<div id="app1">${html1}</div>
<div id="app2">${html2}</div>
`;The renderStatic function automatically configures the Stylesheet for server-side use, but you can also manually configure it.
import { Stylesheet, InjectionMode } from '@uifabric/merge-styles';
// Manual server-side configuration
const stylesheet = Stylesheet.getInstance();
stylesheet.setConfig({
injectionMode: InjectionMode.none, // Don't inject into DOM
namespace: 'myapp' // Add namespace prefix
});
// Reset before each render
stylesheet.reset();
// Render your app
const html = renderToString(<MyApp />);
// Get generated CSS
const css = stylesheet.getRules(true);Import Path:
// Server-side specific import
import { renderStatic } from '@uifabric/merge-styles/lib/server';
// Don't import from main entry point on server
// import { renderStatic } from '@uifabric/merge-styles'; // ❌ WrongEnvironment Detection:
// The library automatically detects server environment
// You can also check manually:
const isServer = typeof document === 'undefined';
if (isServer) {
// Server-side logic
const { html, css } = renderStatic(() => renderToString(<App />));
} else {
// Client-side logic
render(<App />, document.getElementById('root'));
}Express.js Integration:
import express from 'express';
import { renderToString } from 'react-dom/server';
import { renderStatic } from '@uifabric/merge-styles/lib/server';
const app = express();
app.get('*', (req, res) => {
const { html, css } = renderStatic(() => {
return renderToString(<App url={req.url} />);
});
res.send(`
<!DOCTYPE html>
<html>
<head>
<style>${css}</style>
</head>
<body>
<div id="root">${html}</div>
<script src="/client.js"></script>
</body>
</html>
`);
});Gatsby Plugin Integration:
// gatsby-ssr.js
import { renderStatic } from '@uifabric/merge-styles/lib/server';
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString, setHeadComponents }) => {
const { html, css } = renderStatic(() => bodyComponent);
// Inject CSS into head
setHeadComponents([
<style key="merge-styles" dangerouslySetInnerHTML={{ __html: css }} />
]);
// Use extracted HTML
replaceBodyHTMLString(html);
};interface ServerRenderResult {
/** The rendered HTML string */
html: string;
/** The CSS rules generated during rendering */
css: string;
}