Skip to content

Conversation

@RJWadley
Copy link

@RJWadley RJWadley commented Oct 8, 2025

adds turbopack support to @vanilla-extract/next-plugin

turbopack doesn't have any sort of external API yet, so in order to evaluate the contents of our *.css.ts files we need to compile it separately using @vanilla-extract/compiler. To avoid import resolution mismatches, we defer module resolution to turbopack via its webpack-compatible api.

we import css using data URI sources

next/font doesn't work out of the box because turbopack has custom logic for it, so we transform any modules that use it in a css context to contain static references to the generated font family, style, and weight. most other next modules won't work in css.ts files

fixes #1367

@changeset-bot
Copy link

changeset-bot bot commented Oct 8, 2025

🦋 Changeset detected

Latest commit: c075bf3

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@vanilla-extract/turbopack-plugin Minor
@vanilla-extract/next-plugin Minor
@vanilla-extract/compiler Minor
@vanilla-extract/vite-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@csorfab
Copy link

csorfab commented Oct 19, 2025

@mattcompiles @askoufis Can anyone take a look at this? This is a pretty important feature for next.js users. Or should we consider the project dead? Last PR was merged in early August, basically no activity from maintainers since then. Not meaning to bash anyone, I know it's thankless work maintaining open source software, but it would be nice to know what we can expect from this project. Thanks in advance!

@askoufis
Copy link
Contributor

Or should we consider the project dead? Last PR was merged in early August, basically no activity from maintainers since then.

A PR was merged in September. Reviews take time and ongoing work is often slow. All current maintainers have professional commitments and/or families.

I try to look at PRs when I have spare time/motivation, which is often quite sporadic. I'm currently on holiday, but I intend to make some headway on open PRs and repo maintenance in when I get back in early November.

@csorfab
Copy link

csorfab commented Oct 19, 2025

Thanks for your reply! I was a bit too dramatic, sorry about that. Enjoy your holiday, and let me know if I can help with anything to get this PR going.

@RJWadley RJWadley marked this pull request as draft October 23, 2025 04:49
@callum-gander
Copy link

I see there have been some recent commits pushed up to this PR. Are there any bits that you need help with that could be provided to speed up getting this merged in?

Appreciate that it's been a holiday period, but a lot of apps have performance issues with Next which has various potential solutions but the main recommendation seems to boil down to "just use Turbopack" vercel/next.js#48748. Obviously, the lack of support for Turbopack with Vanilla is effectively blocking us from getting those gains, which would be greatly appreciated ahead of Black Friday.

It would be great if there were a list of tasks that I or people within my company could help out with to speed this up, whether that's commits or just QAing. Cheers and thanks for the work on this so far!

@RJWadley
Copy link
Author

RJWadley commented Nov 17, 2025

If you're interested in trying this out, I've been using a preinstall script to download and install this branch:

"preinstall": "pnpm dlx gitpick https://github.com/RJWadley/vanilla-extract/tree/turbo-loader/ /tmp/vanilla-temp -f && cd /tmp/vanilla-temp && pnpm i && pnpm build"

You can install that temporary build with a link:

"@vanilla-extract/next-plugin": "link:/tmp/vanilla-temp/packages/next-plugin",

Although, you probably want to pin to a specific commit instead of the branch for the sake of stability:

"preinstall": "pnpm dlx gitpick RJWadley/vanilla-extract -b f5163f1 /tmp/vanilla-temp -f && cd /tmp/vanilla-temp && pnpm i && pnpm build"

Feedback or issues appreciated! I haven't seen many stats yet on speed or size of builds (compared to webpack) so if you have those I'd love to see some details.


The biggest issue with this at the moment is next specific modules not playing nicely. Since we must process styles outside of next.js there's not much that can be done. Think next/font, next/image, etc. Next has decent webpack loader support, but there are a few things missing that make it much harder (and in some cases impossible) for us to preprocess styles.

I'm gonna do a quick cleanup pass this week to prep this for merge, it's basically ready at this point but is still a bit messy while I'm experimenting with some stuff.

TLDR:

  • if your styles don't use next modules at all everything will work fine
  • next/font partially works
  • next/image imports don't work at all
  • other next modules may or may not work

@callum-gander
Copy link

callum-gander commented Nov 18, 2025

Hmmm, strange, I followed your steps and got this error

@frontend/app:dev:    ▲ Next.js 15.5.0
@frontend/app:dev:    - Local:        http://localhost:3000
@frontend/app:dev:    - Network:      http://10.100.22.2:3000
@frontend/app:dev:    - Environments: .env
@frontend/app:dev:    - Experiments (use with caution):
@frontend/app:dev:      ⨯ reactCompiler
@frontend/app:dev:      · optimizePackageImports
@frontend/app:dev: 
@frontend/app:dev:  ✓ Starting...
@frontend/app:dev: Warning: total number of custom routes exceeds 1000, this can reduce performance. Route counts:
@frontend/app:dev: headers: 5
@frontend/app:dev: rewrites: 2
@frontend/app:dev: redirects: 1246
@frontend/app:dev: See more info: https://nextjs.org/docs/messages/max-custom-routes-reached
@frontend/app:dev: Error: > Couldn't find a `pages` directory. Please create one under the project root
@frontend/app:dev:     at Object.webpack (../../../../../../../../private/tmp/vanilla-temp/packages/next-plugin/dist/vanilla-extract-next-plugin.cjs.dev.js:130:47)

That seemed to point at this specific line in the dist

 const findPagesDirResult = findPagesDir.findPagesDir(dir, ((_resolvedNextConfig$e = resolvedNextConfig.experimental) === null || _resolvedNextConfig$e === void 0 ? void 0 : _resolvedNextConfig$e.appDir) ?? false);

It's probably worth saying that we don't have a pages dir at all, we're fully on app dir. To be fair as well, I'm not sure that this would work for us anyway as we make heavy use of next/image, next/font, as well as other next modules.

When you say it's basically ready, do you mean basically ready minus support for next modules due to dependency on things turbopack isn't yet providing to help you preprocess styles?

If you have a solution to the above error, more than happy to give it a whirl

@RJWadley
Copy link
Author

RJWadley commented Nov 18, 2025

The error looks like resolution error in the webpack plugin setup. Should've seen that coming.
Switch back to the release version of next-plugin, then try configuring the loader manually:

"@vanilla-extract/turbopack-plugin": "link:/tmp/vanilla-temp/packages/turbopack-plugin",
  turbopack: {
    rules: {
      '**/*.css.{ts,tsx,js,jsx}': {
        loaders: [
          {
            loader: '@vanilla-extract/turbopack-plugin',
            options: { nextEnv: {} }, // include env vars set by next.config if any
          },
        ],
      },
    },
  },

As far as next/image support, you're free to style next images as this doesn't route through the plugin:

// page.ts
<Image className={myVanillaClass} />

The specific scenario that doesn't work is importing an image into a css.ts file and using it to generate styles:

// myStyles.css.ts
import coolThing from "./my-cool-image.png"

export const background = style({
  backgroundImage: `url(${coolThing.src})` // fails! use public dir instead
})

This is unlikely to change until we get better tools from turbopack.

For next/font, support is better. Anything under the style property is supported:

// myStyles.css.ts
import myFont from "./typography"

export const background = style({
  fontFamily: myFont.style.fontFamily
})

The specific scenario that doesn't work is extending font classNames in your styles:

// myStyles.css.ts
import myFont from "./typography"

export const background = style(myFont.className) // fails! set font family directly instead

This is also unlikely to change until we get better tools from turbopack.

TLDR using next modules is fine, but using next modules in your styles themselves is likely to fail unless I've specifically worked around it.

@Pnlvfx
Copy link
Contributor

Pnlvfx commented Nov 21, 2025

The error looks like resolution error in the webpack plugin setup. Should've seen that coming. Switch back to the release version of next-plugin, then try configuring the loader manually:

"@vanilla-extract/turbopack-plugin": "link:/tmp/vanilla-temp/packages/turbopack-plugin",
  turbopack: {
    rules: {
      '**/*.css.{ts,tsx,js,jsx}': {
        loaders: [
          {
            loader: '@vanilla-extract/turbopack-plugin',
            options: { nextEnv: {} }, // include env vars set by next.config if any
          },
        ],
      },
    },
  },

As far as next/image support, you're free to style next images as this doesn't route through the plugin:

// page.ts
<Image className={myVanillaClass} />

The specific scenario that doesn't work is importing an image into a css.ts file and using it to generate styles:

// myStyles.css.ts
import coolThing from "./my-cool-image.png"

export const background = style({
  backgroundImage: `url(${coolThing.src})` // fails! use public dir instead
})

This is unlikely to change until we get better tools from turbopack.

For next/font, support is better. Anything under the style property is supported:

// myStyles.css.ts
import myFont from "./typography"

export const background = style({
  fontFamily: myFont.style.fontFamily
})

The specific scenario that doesn't work is extending font classNames in your styles:

// myStyles.css.ts
import myFont from "./typography"

export const background = style(myFont.className) // fails! set font family directly instead

This is also unlikely to change until we get better tools from turbopack.

TLDR using next modules is fine, but using next modules in your styles themselves is likely to fail unless I've specifically worked around it.

For me is fine, thanks, I also had next font issues with webpack so I don't mind this approach,

thank you very much for your work,

I'll try this ASAP

@RJWadley RJWadley marked this pull request as ready for review November 21, 2025 22:59
@callum-gander
Copy link

The error looks like resolution error in the webpack plugin setup. Should've seen that coming. Switch back to the release version of next-plugin, then try configuring the loader manually:

"@vanilla-extract/turbopack-plugin": "link:/tmp/vanilla-temp/packages/turbopack-plugin",
  turbopack: {
    rules: {
      '**/*.css.{ts,tsx,js,jsx}': {
        loaders: [
          {
            loader: '@vanilla-extract/turbopack-plugin',
            options: { nextEnv: {} }, // include env vars set by next.config if any
          },
        ],
      },
    },
  },

As far as next/image support, you're free to style next images as this doesn't route through the plugin:

// page.ts
<Image className={myVanillaClass} />

The specific scenario that doesn't work is importing an image into a css.ts file and using it to generate styles:

// myStyles.css.ts
import coolThing from "./my-cool-image.png"

export const background = style({
  backgroundImage: `url(${coolThing.src})` // fails! use public dir instead
})

This is unlikely to change until we get better tools from turbopack.

For next/font, support is better. Anything under the style property is supported:

// myStyles.css.ts
import myFont from "./typography"

export const background = style({
  fontFamily: myFont.style.fontFamily
})

The specific scenario that doesn't work is extending font classNames in your styles:

// myStyles.css.ts
import myFont from "./typography"

export const background = style(myFont.className) // fails! set font family directly instead

This is also unlikely to change until we get better tools from turbopack.

TLDR using next modules is fine, but using next modules in your styles themselves is likely to fail unless I've specifically worked around it.

Hmmm, so I gave this a try and I was getting .readFile related errors, which I believe was due to the fact that your fixture uses Next 16, whereas we were still on Next 15, so I bumped ours to Next 16 and followed the rest of your steps. It seemed to be fine until we actually tried to load a page and we're now getting these errors

@frontend/app:dev:  ○ Compiling /[store]/[...404] ...
@frontend/app:dev:  ⨯ Error: Styles were unable to be assigned to a file. This is generally caused by one of the following:
@frontend/app:dev: 
@frontend/app:dev: - You may have created styles outside of a '.css.ts' context
@frontend/app:dev: - You may have incorrect configuration. See https://vanilla-extract.style/documentation/getting-started
@frontend/app:dev:     at module evaluation (src/lib/theme/theme.css.ts:15:46)
@frontend/app:dev:     at module evaluation (src/app/[store]/layout.tsx:2:1)
@frontend/app:dev:   13 | } from ".";
@frontend/app:dev:   14 |
@frontend/app:dev: > 15 | export const [themeClass, vars] = createTheme({
@frontend/app:dev:      |                                              ^
@frontend/app:dev:   16 |   color: { ...colors },
@frontend/app:dev:   17 |   space: { ...spacings },
@frontend/app:dev:   18 |   zIndex: { ...zIndexes }, {
@frontend/app:dev:   type: 'Error',
@frontend/app:dev:   page: '/en/theme-test'
@frontend/app:dev: }
@frontend/app:dev:  GET /theme-test 500 in 7.5s (compile: 7.4s, proxy.ts: 56ms, render: 63ms)
@frontend/app:dev:  ⨯ Error: Styles were unable to be assigned to a file. This is generally caused by one of the following:
@frontend/app:dev: 
@frontend/app:dev: - You may have created styles outside of a '.css.ts' context
@frontend/app:dev: - You may have incorrect configuration. See https://vanilla-extract.style/documentation/getting-started
@frontend/app:dev:     at module evaluation (src/lib/theme/theme.css.ts:15:46)
@frontend/app:dev:     at module evaluation (src/app/[store]/layout.tsx:2:1)
@frontend/app:dev:   13 | } from ".";
@frontend/app:dev:   14 |
@frontend/app:dev: > 15 | export const [themeClass, vars] = createTheme({
@frontend/app:dev:      |                                              ^
@frontend/app:dev:   16 |   color: { ...colors },
@frontend/app:dev:   17 |   space: { ...spacings },
@frontend/app:dev:   18 |   zIndex: { ...zIndexes }, {
@frontend/app:dev:   type: 'Error',
@frontend/app:dev:   page: '/en'
@frontend/app:dev: }
@frontend/app:dev:  GET / 500 in 4.3s (compile: 4.3s, proxy.ts: 8ms, render: 5ms)

That's obviously this import { createTheme } from "@vanilla-extract/css"; createTheme. Not sure if again this is due to a misconfiguration, followed your instructions to a tee. I pulled your PR and basically copied over the theme files and created a new test page in the next-16-app-pages-router and it seemed to be working fine so I'm not sure if it is somehow an issue with how I've been testing this

@RJWadley
Copy link
Author

RJWadley commented Nov 25, 2025

yes, I dropped support for next 15 in c1146fe because it dramatically simplifies dependency tracking when the source code has errors.

From the stack trace your code looks good, so it's more likely an issue with the plugin config than an issue with your code. most likely the plugin isn't running on the theme file. if you can manage to throw together a repo that does throw I'd be happy to investigate further. more minimal is obviously better but anything that fails would help diagnose the issue.

Thanks for testing this out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot build a next app with Turbopack (Styles were unable to be assigned to a file)

5 participants