or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

create-instance.mdextract-critical.mdindex.mdrender-to-stream.mdrender-to-string.md
tile.json

create-instance.mddocs/

Custom Instance Creation

Custom emotion server instance creation functionality that enables advanced configurations with specific cache instances, custom keys, nonce values, and compatibility settings.

Capabilities

createEmotionServer Function

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

Multi-tenant Applications

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

Advanced Cache Configuration

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

Content Security Policy Integration

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

Cache Compatibility Settings

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 set

Cache State Management

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

Error Handling and Validation

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