or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

index.mddocs/

Emotion Theming

Emotion Theming is a CSS-in-JS theming solution for React applications, inspired by styled-components. It provides a React context-based theming system that integrates seamlessly with Emotion's styling ecosystem, enabling developers to create consistent design systems and dynamic themes across their applications.

Package Information

  • Package Name: emotion-theming
  • Package Type: npm
  • Language: JavaScript with TypeScript definitions
  • Installation: npm install emotion-theming

Core Imports

import { ThemeProvider, withTheme, useTheme } from "emotion-theming";

For CommonJS:

const { ThemeProvider, withTheme, useTheme } = require("emotion-theming");

Basic Usage

import React from "react";
import { ThemeProvider, useTheme, withTheme } from "emotion-theming";
import styled from "@emotion/styled";

// Define your theme
const theme = {
  colors: {
    primary: "hotpink",
    secondary: "turquoise",
  },
  fonts: {
    body: "system-ui, sans-serif",
    heading: "Georgia, serif",
  },
};

// Use theme in styled components (automatic injection)
const Button = styled.button`
  background-color: ${props => props.theme.colors.primary};
  color: white;
  font-family: ${props => props.theme.fonts.body};
`;

// Use theme with useTheme hook
function ThemedText() {
  const theme = useTheme();
  return (
    <p style={{ color: theme.colors.secondary }}>
      Themed text using useTheme hook
    </p>
  );
}

// Use theme with withTheme HOC
const ThemedDiv = withTheme(({ theme, children }) => (
  <div style={{ fontFamily: theme.fonts.heading }}>
    {children}
  </div>
));

// Wrap your app with ThemeProvider
function App() {
  return (
    <ThemeProvider theme={theme}>
      <ThemedDiv>
        <Button>Click me</Button>
        <ThemedText />
      </ThemedDiv>
    </ThemeProvider>
  );
}

Architecture

Emotion Theming is built around several key patterns:

  • React Context API: Uses @emotion/core's ThemeContext for theme distribution throughout the component tree
  • Theme Merging: Supports nested ThemeProviders with object merging and functional theme transformation
  • Performance Optimization: Uses weak memoization to cache theme computations and prevent unnecessary re-renders
  • TypeScript Integration: Full generic type support for theme objects with proper type inference
  • HOC Pattern: Higher-order component pattern for class component integration
  • React Hooks: Modern hook-based API for functional component integration

Capabilities

Theme Provider

React component that injects theme objects into the component tree via React context. Supports both object-based themes and function-based theme transformations for nested providers.

/**
 * React component that provides theme context to descendant components
 * @param props - Component props including theme and children
 * @returns React element that provides theme context
 */
function ThemeProvider<Theme>(
  props: ThemeProviderProps<Theme>
): React.ReactElement;

interface ThemeProviderProps<Theme> {
  /** Theme object or function that transforms parent theme */
  theme: Partial<Theme> | ((outerTheme: Theme) => Theme);
  /** Child components that will receive theme context */
  children?: React.ReactNode;
}

Usage Examples:

import React from "react";
import { ThemeProvider } from "emotion-theming";

// Object-based theme
const baseTheme = {
  colors: { primary: "blue", secondary: "green" },
  spacing: { small: "8px", medium: "16px" }
};

// Function-based theme for nested providers
const darkTheme = (parentTheme) => ({
  ...parentTheme,
  colors: {
    ...parentTheme.colors,
    primary: "darkblue",
    background: "black"
  }
});

function App() {
  return (
    <ThemeProvider theme={baseTheme}>
      <div>
        {/* Nested provider with theme transformation */}
        <ThemeProvider theme={darkTheme}>
          <DarkModeSection />
        </ThemeProvider>
      </div>
    </ThemeProvider>
  );
}

Theme Hook

React hook that returns the current theme object from the nearest ThemeProvider ancestor. Causes component re-renders when theme changes.

/**
 * React hook that returns the current theme from context
 * @returns Current theme object
 */
function useTheme<Theme>(): Theme;

Usage Examples:

import React from "react";
import { useTheme } from "emotion-theming";

function ThemedComponent() {
  const theme = useTheme();
  
  return (
    <div style={{
      color: theme.colors.primary,
      padding: theme.spacing.medium,
      fontFamily: theme.fonts.body
    }}>
      Content styled with theme
    </div>
  );
}

// With TypeScript for better type safety
interface MyTheme {
  colors: { primary: string; secondary: string; };
  spacing: { small: string; medium: string; };
}

function TypedThemedComponent() {
  const theme = useTheme<MyTheme>();
  // theme is now properly typed as MyTheme
  
  return (
    <button style={{ backgroundColor: theme.colors.primary }}>
      Typed theme usage
    </button>
  );
}

Theme Higher-Order Component

Higher-order component that injects the current theme as a prop into the wrapped component. Useful for class components and complex component wrapping scenarios.

/**
 * Higher-order component that injects theme as a prop
 * @param component - React component to wrap with theme injection
 * @returns Component with theme prop injected
 */
function withTheme<C extends React.ComponentType<any>>(
  component: C
): React.FC<AddOptionalTo<PropsOf<C>, 'theme'>>;

Usage Examples:

import React from "react";
import { withTheme } from "emotion-theming";

// Class component usage
class ClassButton extends React.Component {
  render() {
    const { theme, children, ...otherProps } = this.props;
    return (
      <button
        style={{
          backgroundColor: theme.colors.primary,
          color: theme.colors.text,
          padding: theme.spacing.medium
        }}
        {...otherProps}
      >
        {children}
      </button>
    );
  }
}

const ThemedClassButton = withTheme(ClassButton);

// Functional component usage (alternative to useTheme)
const FunctionalButton = ({ theme, children, ...props }) => (
  <button
    style={{
      backgroundColor: theme.colors.secondary,
      borderRadius: theme.borderRadius
    }}
    {...props}
  >
    {children}
  </button>
);

const ThemedFunctionalButton = withTheme(FunctionalButton);

// Usage in JSX
function App() {
  return (
    <div>
      <ThemedClassButton>Class Component Button</ThemedClassButton>
      <ThemedFunctionalButton>Functional Component Button</ThemedFunctionalButton>
    </div>
  );
}

Error Handling

Emotion Theming includes development-time validation with helpful error messages:

// Theme must be an object or function
<ThemeProvider theme={null} /> // Error: Please make your theme prop a plain object

// Theme functions must return objects
<ThemeProvider theme={() => null} /> // Error: Please return an object from your theme function

// Arrays are not valid themes
<ThemeProvider theme={[]} /> // Error: Please make your theme prop a plain object

Types

interface ThemeProviderProps<Theme> {
  theme: Partial<Theme> | ((outerTheme: Theme) => Theme);
  children?: React.ReactNode;
}

interface EmotionTheming<Theme> {
  ThemeProvider(props: ThemeProviderProps<Theme>): React.ReactElement;
  withTheme<C extends React.ComponentType<any>>(
    component: C
  ): React.FC<AddOptionalTo<PropsOf<C>, 'theme'>>;
}

type PropsOf<
  C extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>
> = JSX.LibraryManagedAttributes<C, React.ComponentPropsWithRef<C>>;

type Omit<T, U> = T extends any ? Pick<T, Exclude<keyof T, U>> : never;

type AddOptionalTo<T, U> = Omit<T, U> & Partial<Pick<T, Extract<keyof T, U>>>;

Integration with Emotion Ecosystem

Emotion Theming integrates seamlessly with other Emotion packages:

With @emotion/styled

import styled from "@emotion/styled";
import { ThemeProvider } from "emotion-theming";

// Theme is automatically injected into styled components
const Button = styled.button`
  background-color: ${props => props.theme.colors.primary};
  padding: ${props => props.theme.spacing.medium};
`;

// No need to manually pass theme - it's injected automatically
<ThemeProvider theme={theme}>
  <Button>Automatically themed</Button>
</ThemeProvider>

With @emotion/core css prop

/** @jsx jsx */
import { jsx } from "@emotion/core";
import { ThemeProvider } from "emotion-theming";

function MyComponent() {
  return (
    <div
      css={theme => ({
        color: theme.colors.primary,
        fontSize: theme.typography.body
      })}
    >
      CSS prop with theme function
    </div>
  );
}

<ThemeProvider theme={theme}>
  <MyComponent />
</ThemeProvider>

Common Patterns

Conditional Theming

const lightTheme = { mode: 'light', colors: { bg: 'white', text: 'black' } };
const darkTheme = { mode: 'dark', colors: { bg: 'black', text: 'white' } };

function App({ isDark }) {
  return (
    <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
      <MyApp />
    </ThemeProvider>
  );
}

Theme Inheritance and Overrides

const baseTheme = {
  colors: { primary: 'blue', secondary: 'green' },
  spacing: { small: '8px', medium: '16px' }
};

function App() {
  return (
    <ThemeProvider theme={baseTheme}>
      <Header />
      {/* Override specific colors for this section */}
      <ThemeProvider theme={parent => ({ 
        ...parent, 
        colors: { ...parent.colors, primary: 'red' } 
      })}>
        <AlertSection />
      </ThemeProvider>
    </ThemeProvider>
  );
}

Performance Optimization

// ✅ Good: Theme object is hoisted and stable
const theme = { colors: { primary: 'blue' } };

function App() {
  return (
    <ThemeProvider theme={theme}>
      <Content />
    </ThemeProvider>
  );
}

// ❌ Bad: Theme object is recreated on every render
function BadApp() {
  return (
    <ThemeProvider theme={{ colors: { primary: 'blue' } }}>
      <Content />
    </ThemeProvider>
  );
}