CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-uifabric--merge-styles

Style loading utilities that provide CSS-in-JS functionality with automatic vendor prefixing, RTL support, and server-side rendering capabilities.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

server-rendering.mddocs/

Server-Side Rendering

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.

Capabilities

renderStatic Function

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;

Server-Side Rendering Considerations

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'));

Namespace Collision Prevention

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>
`;

Configuration for Server-Side Rendering

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);

Server Environment Setup

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'; // ❌ Wrong

Environment 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'));
}

Common Patterns

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);
};

Return Types

interface ServerRenderResult {
  /** The rendered HTML string */
  html: string;
  /** The CSS rules generated during rendering */
  css: string;
}

docs

configuration.md

font-animation.md

index.md

server-rendering.md

style-sets.md

styling.md

tile.json