Skip to content

Memory leak in VariableContextProvider when re-rendering with CSS variables #245

@alias-mac

Description

@alias-mac

Describe the bug

There is a memory leak in VariableContextProvider that causes memory to grow continuously with each component re-render. The memory is never released even after garbage collection, and the leak scales with the number of CSS variables passed to the provider.

Reproduction
Repository: https://github.com/alias-mac/nativewind-mem-leak

To reproduce:

  1. Clone the reproduction repository
  2. Run npm install and start the app with npm run ios or npm run android
  3. Open your memory profiler (Xcode Instruments, Android Studio Profiler, or React DevTools)
  4. Click the "Clicked X times" button repeatedly (50-100 times)
  5. Observe memory usage continuously growing without being released

The reproduction includes:

  • A ThemeProvider component using VariableContextProvider with 1000 CSS variables (these are just examples)
  • A simple counter component that re-renders on each click
  • Components using className with the CSS variables (e.g., text-primary)
  • The leak occurs regardless of animations - tested with and without animate-pulse

Key files:

  • /components/ThemeProvider.tsx - Shows VariableContextProvider with many CSS variables
  • /app/index.tsx - Simple re-rendering component with key={count} to force remounts
  • /global.css - CSS variable definition with --color-primary: var(--foo-primary)

Expected behavior
Memory should remain stable or grow minimally with re-renders. After garbage collection, memory should return to baseline levels. CSS variable context should be cleaned up when components unmount or update.

Additional context

  • Using NativeWind v5 (preview) with Tailwind CSS v4
  • The leak appears to be related to how VariableContextProvider manages CSS variable context internally
  • The severity increases with the number of CSS variables passed to the provider (tested with 1000 variables and leaking 20.5kB on each re-render, but our app has about 40kB for this)
  • Each re-render appears to create new references that are not properly garbage collected
  • Adding key={count} to force component remounting makes the leak more pronounced
Image

After only 4 "clicks/re-renders" it increased the size of the DictPropertyMap by 38:

Image

We were able to validate that this issue is reproducible in the production version of the app, so it isn't based on the dev mode (with HMR, dev-client or other developer tools enabled).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions