npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

unplugin-inline-css-modules

v0.1.4

Published

Zero-runtime scoped CSS for any JS framework — write CSS modules inline as tagged templates

Downloads

583

Readme

unplugin-inline-css-modules

npm License: MIT

Zero-runtime scoped CSS, targeting any framework on any bundler.

A simple example:

import { css } from 'unplugin-inline-css-modules'

const classes = css`
  .button {
    background-color: #1f1e33;
    color: white;
    padding: 1rem 2rem;
    border-radius: 4px;
  }
`

export const Button = () => <button className={classes.button}>Click me</button>

At build time, the CSS is extracted into a real CSS module. Existing PostCSS syntax, Tailwind @apply directives, and CSS tooling all work out of the box however they were configured in the bundler.

Why

Frameworks like Vue have <style scoped>. The rest of us have been stuck choosing between separate .module.css files (context-switching) and CSS-in-JS libraries (runtime overhead, incompatible with PostCSS tooling).

This plugin gives you the best of both worlds: co-located styles with zero runtime cost. Under the hood it just generates CSS modules, so your entire CSS toolchain works without any changes.

Install

npm install unplugin-inline-css-modules

Setup

Add the plugin for your bundler:

// vite.config.ts
import { defineConfig } from 'vite'
import inlineCSSModules from 'unplugin-inline-css-modules/vite'

export default defineConfig({
  plugins: [inlineCSSModules()],
})
// rollup.config.js
import inlineCSSModules from 'unplugin-inline-css-modules/rollup'

export default {
  plugins: [inlineCSSModules()],
}
// rolldown.config.js
import inlineCSSModules from 'unplugin-inline-css-modules/rolldown'

export default {
  plugins: [inlineCSSModules()],
}
// webpack.config.js
import inlineCSSModules from 'unplugin-inline-css-modules/webpack'

export default {
  plugins: [inlineCSSModules()],
}
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core'
import inlineCSSModules from 'unplugin-inline-css-modules/rspack'

export default defineConfig({
  tools: {
    rspack: {
      plugins: [inlineCSSModules()],
    },
  },
})
import { build } from 'esbuild'
import inlineCSSModules from 'unplugin-inline-css-modules/esbuild'

build({
  plugins: [inlineCSSModules()],
})

Farm is an experimental target. There has not been thorough testing for functionality.

// farm.config.ts
import inlineCSSModules from 'unplugin-inline-css-modules/farm'

export default {
  vitePlugins: [inlineCSSModules()],
}
import inlineCSSModules from 'unplugin-inline-css-modules/bun'

Bun.build({
  plugins: [inlineCSSModules()],
})

Framework Integrations

// next.config.ts
import type { NextConfig } from 'next'
import inlineCSSModules from 'unplugin-inline-css-modules/next'

const nextConfig: NextConfig = {
  webpack: config => {
    config.plugins = config.plugins || []
    config.plugins.push(inlineCSSModules())
    return config
  },
}

export default nextConfig

Note: SWC breaks some assumptions for virtual module resolution. As a workaround, CSS modules are cached in node_modules/.cache/inline-css-modules/. If you reinstall node_modules, remove the .next folder to clear stale references.

// nuxt.config.ts
import inlineCSSModules from 'unplugin-inline-css-modules/vite'

export default defineNuxtConfig({
  vite: {
    plugins: [inlineCSSModules()],
  },
})
// astro.config.mjs
import { defineConfig } from 'astro/config'
import inlineCSSModules from 'unplugin-inline-css-modules/astro'

export default defineConfig({
  integrations: [inlineCSSModules()],
})
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
import inlineCSSModules from 'unplugin-inline-css-modules/vite'

export default defineConfig({
  plugins: [inlineCSSModules(), sveltekit()],
})

Usage Examples

React

import { css } from 'unplugin-inline-css-modules'

const classes = css`
  .root {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
  }

  .button {
    background-color: #1f1e33;
    color: white;
    padding: 1rem 2rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
`

export const Root = () => (
  <div className={classes.root}>
    <button className={classes.button}>Click me</button>
  </div>
)

Vue

<script setup lang="ts">
import { css } from 'unplugin-inline-css-modules'

const classes = css`
  .container {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
  }

  .button {
    background-color: #1f1e33;
    color: white;
    padding: 1rem 2rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
`
</script>

<template>
  <div :class="classes.container">
    <button :class="classes.button">Click me</button>
  </div>
</template>

Solid

import type { Component } from 'solid-js'
import { css } from 'unplugin-inline-css-modules'

const styles = css`
  .container {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
  }

  .button {
    background-color: #1f1e33;
    color: white;
    padding: 1rem 2rem;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }
`

const App: Component = () => (
  <div class={styles.container}>
    <button class={styles.button}>Click me</button>
  </div>
)

export default App

Svelte

<script lang="ts">
  import { css } from 'unplugin-inline-css-modules'

  const classes = css`
    .button {
      background-color: #1f1e33;
      color: white;
      padding: 1rem 2rem;
      border-radius: 4px;
    }
  `
</script>

<button class={classes.button}>Click me</button>

Astro

---
import { css } from 'unplugin-inline-css-modules'

const classes = css`
  .button {
    background-color: #1f1e33;
    color: white;
    padding: 1rem 2rem;
    border-radius: 4px;
  }
`
---

<button class={classes.button}>Click me</button>

How It Works

At build time, the plugin transforms your css tagged template literals into real CSS module imports:

// What you write:
const classes = css`
  .root {
    color: red;
  }
`

// What gets compiled:
import classes from 'virtual:inline-css-modules/App-0.module.css'

The CSS gets extracted into a virtual module and then processed through your bundler's normal CSS pipeline. This means PostCSS plugins, Tailwind @apply, preprocessors, and any other CSS tooling work exactly as they would with a regular .module.css file.

Options

| Option | Type | Default | Description | | -------------- | ------------------------------------------------------------------------------------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------------- | | tagName | string | 'css' | Template tag name to match. Useful for avoiding conflicts with other CSS-in-JS libraries. | | fileMatch | RegExp | /\.(tsx\|jsx\|js\|vue\|svelte)$/ | Pattern for files to transform. | | extension | 'css' \| 'scss' \| 'sass' \| 'styl' \| 'less' \| (filename: string) => SupportedExtension | 'css' | CSS preprocessor to use. Can be a string or a function that returns the extension based on the filename. | | inlineImport | boolean | true | When false, generated imports are hoisted to the top of the file instead of replacing the declaration inline. |

Caveats

No string interpolation. The css tag looks like a template literal, but it's a compile-time transform. The contents are moved into a real CSS module, so dynamic values can't work.

Class variables are replaced at compile time. The const classes = css\...`` declaration is replaced with an import statement, so you can't reassign or manipulate the variable at runtime.

Troubleshooting

css is not defined or similar errors -- Make sure the tagName option matches the tag you're using in your code. The plugin removes the import from unplugin-inline-css-modules and replaces the tagged template with a CSS module import. If the tag names don't match, the import gets removed but the template isn't transformed.

License

MIT