Custom emotion server instance creation functionality that enables advanced configurations with specific cache instances, custom keys, nonce values, and compatibility settings.
Factory function that creates custom emotion server instances with specific cache configurations, enabling advanced server-side rendering scenarios and multi-tenant applications.
/**
* Creates a custom emotion server instance with specified cache
* @param cache - EmotionCache instance with custom configuration
* @returns EmotionServer object with all three rendering methods
*/
function createEmotionServer(cache: EmotionCache): EmotionServer;
interface EmotionServer {
extractCritical(html: string): EmotionCritical;
renderStylesToString(html: string): string;
renderStylesToNodeStream(): NodeJS.ReadWriteStream;
}
interface EmotionCache {
key: string; // CSS class prefix (e.g., "css", "emotion")
inserted: Record<string, string | true>; // Map of style IDs to CSS or true for registered styles
registered: Record<string, string>; // Map of registered style names to IDs
sheet: StyleSheet; // StyleSheet instance for DOM manipulation
nonce?: string; // Content Security Policy nonce
compat?: true; // Compatibility mode for legacy versions
insert( // Method to insert styles into cache
selector: string,
serialized: SerializedStyles,
sheet: StyleSheet,
shouldCache: boolean
): string | void;
}
interface StyleSheet {
container: HTMLElement;
nonce?: string;
key: string;
insert(rule: string): void;
flush(): void;
tags: Array<HTMLStyleElement>;
}
interface SerializedStyles {
name: string;
styles: string;
map?: string;
next?: SerializedStyles;
}Usage Examples:
import createEmotionServer from "@emotion/server/create-instance";
import createCache from "@emotion/cache";
// Create cache with custom configuration
const cache = createCache({
key: "app",
nonce: "security-nonce-12345"
});
// Create server instance
const { extractCritical, renderStylesToString, renderStylesToNodeStream } = createEmotionServer(cache);
// Use custom instance
const { html, css, ids } = extractCritical(renderToString(<App />));Separate Cache per Tenant:
import createEmotionServer from "@emotion/server/create-instance";
import createCache from "@emotion/cache";
// Create separate caches for different tenants
const tenantCaches = {
tenant1: createCache({ key: "t1", nonce: "nonce-tenant1" }),
tenant2: createCache({ key: "t2", nonce: "nonce-tenant2" }),
tenant3: createCache({ key: "t3", nonce: "nonce-tenant3" })
};
// Create server instances for each tenant
const tenantServers = Object.fromEntries(
Object.entries(tenantCaches).map(([tenantId, cache]) => [
tenantId,
createEmotionServer(cache)
])
);
// Use tenant-specific server
const renderForTenant = (tenantId, component) => {
const server = tenantServers[tenantId];
return server.renderStylesToString(renderToString(component));
};Custom Key and Prefix:
// Create cache with custom CSS class prefix
const customCache = createCache({
key: "myapp", // Results in classes like "myapp-abc123"
prefix: true // Enables CSS prefix for better isolation
});
const server = createEmotionServer(customCache);
// Generated CSS classes will use "myapp" prefix
const html = server.renderStylesToString(renderToString(<App />));
// Output includes: class="myapp-abc123"Container-based Styling:
// Create cache that targets specific container
const containerCache = createCache({
key: "widget",
container: document.querySelector("#widget-container")
});
const widgetServer = createEmotionServer(containerCache);Nonce-based CSP:
import crypto from "crypto";
// Generate unique nonce for each request
const generateNonce = () => crypto.randomBytes(16).toString("base64");
const createSecureServer = (nonce) => {
const cache = createCache({
key: "secure",
nonce: nonce
});
return createEmotionServer(cache);
};
// Express.js middleware
app.use((req, res, next) => {
const nonce = generateNonce();
req.emotionNonce = nonce;
// Set CSP header
res.setHeader(
"Content-Security-Policy",
`style-src 'self' 'nonce-${nonce}';`
);
next();
});
app.get("/", (req, res) => {
const server = createSecureServer(req.emotionNonce);
const html = server.renderStylesToString(renderToString(<App />));
res.send(html);
});Legacy Compatibility Mode:
// Enable compatibility mode for older Emotion versions
const legacyCache = createCache({
key: "emotion",
compat: true // Automatically set by createEmotionServer if not specified
});
const legacyServer = createEmotionServer(legacyCache);
// The server automatically sets compat: true if not already setInspecting Cache State:
const cache = createCache({ key: "debug" });
const server = createEmotionServer(cache);
// Render some components to populate cache
server.renderStylesToString(renderToString(<App />));
// Inspect cache contents
console.log("Inserted styles:", Object.keys(cache.inserted));
console.log("Registered components:", Object.keys(cache.registered));
// Clear cache for next render
Object.keys(cache.inserted).forEach(key => {
delete cache.inserted[key];
});Cache Hydration:
// Server-side: serialize cache state
const renderWithCache = (component) => {
const cache = createCache({ key: "hydrate" });
const server = createEmotionServer(cache);
const html = server.renderStylesToString(renderToString(component));
// Serialize cache for client
const cacheState = {
inserted: cache.inserted,
registered: cache.registered
};
return { html, cacheState };
};
// Client-side: restore cache state
const hydrateWithCache = (cacheState) => {
const cache = createCache({ key: "hydrate" });
// Restore cache state
Object.assign(cache.inserted, cacheState.inserted);
Object.assign(cache.registered, cacheState.registered);
return cache;
};Cache Validation:
const createValidatedServer = (cacheConfig) => {
try {
const cache = createCache(cacheConfig);
// Validate cache properties
if (!cache.key) {
throw new Error("Cache key is required");
}
if (typeof cache.inserted !== "object") {
throw new Error("Invalid cache.inserted property");
}
return createEmotionServer(cache);
} catch (error) {
console.error("Failed to create emotion server:", error);
throw error;
}
};Runtime Cache Monitoring:
const createMonitoredServer = (cache) => {
const server = createEmotionServer(cache);
// Wrap methods with monitoring
const originalExtractCritical = server.extractCritical;
server.extractCritical = (html) => {
const start = Date.now();
const result = originalExtractCritical(html);
const duration = Date.now() - start;
console.log(`extractCritical took ${duration}ms, found ${result.ids.length} styles`);
return result;
};
return server;
};