Stoop
GitHub
/

Migration from Stitches

A complete guide to migrating from Stitches to Stoop, including feature comparison and step-by-step instructions.

Why Migrate?

Stoop is designed as a modern, actively maintained replacement for Stitches with key improvements:

Feature Comparison

FeatureStitchesStoopMigration Impact
Core APIcreateStitchescreateStoopSimple rename
Theme tokens$token syntax$token syntax Same
Variants APIvariants property (2nd param)variants property (2nd param) Same
Compound variantsSupportedNot supported Breaking change
Theme systemRuntime valuesCSS variables Better (instant switching)
Multi-themeManual managementBuilt-in Provider Better (easier)
SSR supportYesYes Same
Next.js App RouterLimitedFull support Better
Runtime CSS generationYesYes Same
Utility functionsMany built-insFlexible system Same capabilities

Key Differences

Theme System

Stitches: Theme tokens resolve to runtime values injected via JavaScript.

Stoop: Theme tokens resolve to CSS variables (var(--colors-primary)), enabling instant theme switching without recompiling CSS.

Multi-Theme Support

Stitches: Requires manual theme management with CSS class switching.

Stoop: Built-in multi-theme support via Provider component with automatic CSS variable switching.

Compound Variants

Stitches: Supports compound variants (combining multiple variant conditions).

Stoop: Does not currently support compound variants (planned for future release). Variants work independently, which is sufficient for most use cases. Achieve similar results using conditional css props or wrapper components.

Utility Functions

Stitches: Advanced utility function system with extensive built-in utilities.

Stoop: Flexible utility function system with the same capabilities. Create any custom utilities you need. Stitches provides more built-in utilities out of the box, but Stoop's utility system is equally powerful and flexible.

Migration Steps

Step 1: Install Stoop

npm uninstall @stitches/react
npm install stoop

Step 2: Update Theme Configuration

Before (Stitches):

import { createStitches } from '@stitches/react';

const { styled, css, globalCss, keyframes, getCssText } = createStitches({
  theme: {
    colors: {
      primary: '#0070f3',
      secondary: '#7928ca',
      background: '#ffffff',
      text: '#000000',
    },
    space: {
      small: '8px',
      medium: '16px',
      large: '24px',
    },
  },
  media: {
    bp1: '(min-width: 640px)',
    bp2: '(min-width: 768px)',
  },
  utils: {
    mx: (value) => ({
      marginLeft: value,
      marginRight: value,
    }),
  },
});

After (Stoop):

// stoop.theme.ts (or theme.ts) - REQUIRED CONVENTION
import { createStoop } from 'stoop';

// Define your themes
const lightTheme = {
  colors: {
    primary: '#0070f3',
    secondary: '#7928ca',
    background: '#ffffff',
    text: '#000000',
  },
  space: {
    small: '8px',
    medium: '16px',
    large: '24px',
  },
};

const darkTheme = {
  colors: {
    primary: '#3291ff',
    background: '#000000',
    text: '#ffffff',
  },
  // Other scales (space, etc.) inherit from lightTheme
};

// Create Stoop instance at module level (required convention)
const stoop = createStoop({
  theme: lightTheme, // Default theme
  themes: {
    light: lightTheme,
    dark: darkTheme,
  },
  media: {
    bp1: '(min-width: 640px)',
    bp2: '(min-width: 768px)',
  },
  utils: {
    mx: (value) => ({
      marginLeft: value,
      marginRight: value,
    }),
  },
});

// Export APIs throughout your app
export const { styled, css, globalCss, keyframes, getCssText, Provider, useTheme } = stoop;

Key Changes:

Step 3: Component Variants

Variants work exactly the same way - no changes needed.

const Button = styled('button', {
  padding: '$medium',
  borderRadius: '$default',
  variants: {
    size: {
      small: { padding: '$small' },
      large: { padding: '$large' },
    },
    variant: {
      primary: { backgroundColor: '$primary' },
      secondary: { backgroundColor: '$secondary' },
    },
  },
});

Step 4: Handle Compound Variants

Stoop does not currently support compound variants (planned for future release). Most projects don't use compound variants, and regular variants are usually sufficient. If you use compound variants, refactor them using one of these approaches:

Before (Stitches):

const Button = styled('button', {
  variants: {
    size: { small: {}, large: {} },
    variant: { primary: {}, secondary: {} },
  },
  compoundVariants: [
    {
      size: 'small',
      variant: 'primary',
      css: { fontWeight: '$bold', textTransform: 'uppercase' },
    },
  ],
});

Option 1: Use conditional css prop (Recommended)

const Button = styled('button', {
  variants: {
    size: { small: {}, large: {} },
    variant: { primary: {}, secondary: {} },
  },
});

// In your component
function MyButton({ size, variant, ...props }) {
  const isSmallPrimary = size === 'small' && variant === 'primary';

  return (
    <Button
      size={size}
      variant={variant}
      css={isSmallPrimary ? { fontWeight: '$bold', textTransform: 'uppercase' } : undefined}
      {...props}
    />
  );
}

Option 2: Create a wrapper component

const ButtonBase = styled('button', {
  variants: {
    size: { small: {}, large: {} },
    variant: { primary: {}, secondary: {} },
  },
});

const Button = ({ size, variant, ...props }) => {
  const compoundStyles = size === 'small' && variant === 'primary'
    ? { fontWeight: '$bold', textTransform: 'uppercase' }
    : undefined;

  return <ButtonBase size={size} variant={variant} css={compoundStyles} {...props} />;
};

Option 3: Use separate variant values

const Button = styled('button', {
  variants: {
    size: {
      small: {},
      'small-primary': { fontWeight: '$bold', textTransform: 'uppercase' },
      large: {},
    },
    variant: { primary: {}, secondary: {} },
  },
});

Step 5: Update Imports

Before (Stitches):

import { styled, css } from '@stitches/react';

After (Stoop):

import { styled, css } from './stoop.theme'; // or wherever you export from

Step 6: Update SSR Setup

Next.js Pages Router

Before (Stitches):

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
import { getCssText } from '../stitches.config';

export default function Document() {
  return (
    <Html>
      <Head>
        <style id="stitches" dangerouslySetInnerHTML={{ __html: getCssText() }} />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

After (Stoop):

// pages/_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';
import { getCssText } from '../stoop.theme';

export default function Document() {
  return (
    <Html>
      <Head>
        <style id="stoop-styles" dangerouslySetInnerHTML={{ __html: getCssText() }} />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Key Change: getCssText() no longer accepts a theme parameter - it includes all configured themes.

Next.js App Router

Before (Stitches): Stitches has limited support for App Router.

After (Stoop):

// app/components/Styles.tsx
'use client';

import { useServerInsertedHTML } from 'next/navigation';
import { getCssText } from '../stoop.theme';

export function Styles() {
  useServerInsertedHTML(() => {
    return (
      <style
        id="stoop-styles"
        dangerouslySetInnerHTML={{ __html: getCssText() }}
        suppressHydrationWarning
      />
    );
  });
  return null;
}

// app/layout.tsx
import { Styles } from './components/Styles';
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Styles />
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Stoop provides a built-in Provider component for theme management.

Before (Stitches): No built-in Provider - manual theme management required.

After (Stoop):

// app/providers.tsx
'use client';

import { Provider } from './stoop.theme';

export function Providers({ children }) {
  return <Provider>{children}</Provider>;
}

Step 8: Update Theme Switching

Before (Stitches):

// Manual theme management
const darkTheme = createTheme('dark-theme', { ... });
// Manual CSS variable updates

After (Stoop):

// app/components/ThemeToggle.tsx
'use client';

import { useTheme } from './stoop.theme';

export function ThemeToggle() {
  const { themeName, toggleTheme } = useTheme();

  return (
    <button onClick={toggleTheme}>
      Current theme: {themeName}
    </button>
  );
}

What Stays the Same

These features work identically:

Breaking Changes Summary

  1. Compound Variants - Not currently supported (planned for future) - use conditional logic or css prop (most projects don't need compound variants)
  2. Theme Configuration - Add themes object for multi-theme support (optional but recommended)
  3. getCssText() - No longer accepts theme parameter - includes all themes (simpler API)
  4. Multi-theme - Requires Provider wrapper (but much easier to use than manual theme management)

Troubleshooting

Issue: Compound variants not working

Solution: Refactor to use conditional css prop, wrapper component, or separate variant values. See Step 4 above.

Issue: Theme switching not working

Solution: Make sure you're using the Provider component and useTheme() hook. Provider must wrap your app.

Issue: SSR styles not appearing

Solution: Ensure getCssText() is called in your document/layout and styles are injected before React hydrates. For App Router, use useServerInsertedHTML.

Issue: Type errors with variants

Solution: Variants work the same way as Stitches. Ensure your variants object follows the same structure.

Issue: Theme tokens not resolving

Solution: Make sure your theme configuration includes all scales you're using. Stoop uses CSS variables, so tokens resolve at runtime.

Migration Time Estimate

Factors affecting time:

After Migration

  1. Test all components thoroughly
  2. Verify theme switching works
  3. Check SSR rendering
  4. Update any custom utilities if needed
  5. Consider using Stoop's built-in multi-theme features