vuejs/core · error · Error

[@vue/compiler-sfc] `modules` option can only be used with c

Error message

[@vue/compiler-sfc] `modules` option can only be used with compileStyleAsync().

What it means

Thrown by doCompileStyle in @vue/compiler-sfc when the `modules` option is used with the synchronous compileStyle() entry point. postcss-modules resolves asynchronously (it writes a JSON map via getJSON), so Vue requires the caller to use compileStyleAsync and to set options.isAsync. The guard at compileStyle.ts:129 fires after the browser-build check, before postcssModules is registered.

Source

Thrown at packages/compiler-sfc/src/compileStyle.ts:129

  const longId = `data-v-${shortId}`

  const plugins = (postcssPlugins || []).slice()
  plugins.unshift(cssVarsPlugin({ id: shortId, isProd }))
  if (trim) {
    plugins.push(trimPlugin())
  }
  if (scoped) {
    plugins.push(scopedPlugin(longId))
  }
  let cssModules: Record<string, string> | undefined
  if (modules) {
    if (__GLOBAL__ || __ESM_BROWSER__) {
      throw new Error(
        '[@vue/compiler-sfc] `modules` option is not supported in the browser build.',
      )
    }
    if (!options.isAsync) {
      throw new Error(
        '[@vue/compiler-sfc] `modules` option can only be used with compileStyleAsync().',
      )
    }
    plugins.push(
      postcssModules({
        ...modulesOptions,
        getJSON: (_cssFileName: string, json: Record<string, string>) => {
          cssModules = json
        },
      }),
    )
  }

  const postCSSOptions: ProcessOptions = {
    ...postcssOptions,
    to: filename,
    from: filename,
  }

View on GitHub (pinned to a2b40db9a8)

Solutions

  1. Switch to compileStyleAsync() and await its result when you need CSS Modules.
  2. If you do not actually need CSS Modules, remove the `modules: true` option and keep using the sync compileStyle().
  3. In integration code, branch on the presence of the modules option to pick the correct entry point.

Example fix

// before
import { compileStyle } from '@vue/compiler-sfc'
const res = compileStyle({ filename: 'x.css', source, modules: true })

// after
import { compileStyleAsync } from '@vue/compiler-sfc'
const res = await compileStyleAsync({ filename: 'x.css', source, modules: true })
Defensive patterns

Strategy: validation

Validate before calling

// Route the modules option to the async entry automatically.
async function compileStyleSmart(opts) {
  if (opts.modules) {
    return compileStyleAsync({ ...opts, isAsync: true })
  }
  return compileStyle(opts)
}

Type guard

function needsAsyncCompile(opts: unknown): opts is { modules: true } {
  return !!opts && (opts as any).modules === true
}

Prevention

When it happens

Trigger: Calling compileStyle({ modules: true }) (the sync function) instead of compileStyleAsync({ modules: true }). Using any wrapper that internally calls the sync compileStyle while forwarding a modules flag.

Common situations: Migrating a style pipeline from sync to async but forgetting to switch the entry function; a custom integration that re-exports compileStyle under a different name.

Related errors


AI-assisted analysis of vuejs/core@a2b40db9a8 (2026-08-12). Data as JSON: /api/errors/24f3e196e668b9f3. Report an issue: GitHub.