[Guide] Experimental Vite Support in Redwood v4.1


Experimental Vite Support is now included in Redwood v4.1.0.

With Redwood v4.1 RC, we’re launching support for switching your bundler from the default Webpack to Vite 4!

:information_source: To use 4.1 RC, make sure you’ve upgraded to v4 of Redwood first, and made all the relevant changes to your code. Redwood v4.0.0 Upgrade Guide

Vite support is still in the experimental phase, so we really, I mean, realllly… value your feedback from trying it out! (It is currently opt-in). Switching a bundler, especially with a batteries-included framework like Redwood is challenging. So, your help in trialing and reporting issues is incredibly valuable.

Importantly: if you choose to stay on Webpack, you can totally continue to with V4.x, no need to do anything!

Video Walk-through

What you get in this release:

  • a setup command to convert your project to use Vite
  • the dev server and build process, for the web side, will be switched over to Vite. You can expect a big performance boost, especially during dev

Some gotchas:

  • Prerendered images can cause a flash. This happens because the image in the HTML is replaced by a different one from Vite’s build proess once your app has loaded.
  • Storybook still runs on Webpack: expect Vite support in Redwood’s storybook to come soon. This actually has no real impact, minus the fact that you don’t get the performance boost from Vite that you do during dev.
  • There are subtle differences in the way environment variables are loaded. If you have specified a variable in your redwood.toml, in the includeEnvironmentVariables array, if the value is not specified, the dev and build process will crash (and tell you which vars are missing)

Opt-in guide

  1. Make sure you’re running the latest RC of Redwood.
    Run yarn rw upgrade -t rc. Vite suppport is only available in 4.1-rc (or higher)

  2. Setup Vite by running yarn rw setup vite.
    This will configure your project to switch over to Vite. Take a look at your git diff to see the changes introduced!

  3. Launch dev server
    Run yarn rw dev. The console output should be slightly different to what you’re used to, and should look like this!

  1. Launch your browser (usually on localhost:8910) but may be different on your project! If you see a blank screen at this point, something has probably gone wrong, and it would be incredibly useful if you would raise an issue with the error(s) printed in your terminal.
ℹ️ Getting a vite-plugin-environment error? Expand for details

With our Vite setup, if you are receiving an error like “Error: vite-plugin-environment: the BAZINGA environment variable is undefined.” - its indicating a missing environment variable. You have to define BAZINGA in your .env or .env.defaults file

This happens because in your redwood.toml you specified that you want to include a variable

  bundler = "vite" 
  includeEnvironmentVariables = ['BAZINGA'] # 👈 Included here

But haven’t defined it in your .env files

:tophat: Note that there may be Vite specific quirks - often these have a quick and easy solution if you do a search on the Vite github issues, but we’d appreciate if you raised an issue on the Redwood repo with the solution to share with the community!

Switching between Webpack and Vite

Once you’ve setup Vite, you’ll notice a new flag in your redwood.toml

+ bundler = "vite" # 👈 new bundler flag
  port = 8910
  apiUrl = "/api"
  includeEnvironmentVariables = ['CONTEXT', 'NODE_ENV', 'DEPLOY_ID']
  title = "My Redwood App"

If you comment out the bundler, or switch it to "webpack", it will switch the bundler back!

This might come in handy, if you find bugs with the Vite integration and need to continue development!

Discord Channel For Support

We’ve setup a special Discord channel, where you can report and discuss any issues you may be facing!

Please do remember to take a bit of time to describe your problem in detail, and include any relevant errors/warnings and ideally a reproduction on Github or Gitpod!


Was waiting for this release! Am curious about it!
Nice that you stick to process.env. variables!

I am on Windows and unfortunately experiencing an issue with loading the entry-client.jsx. It say "Not allowed to load local resource: file:///C:...entry-client.jsx".

Now I just went ahead and changed the index redwoodjs/vite file in node_modules. So that the file part is not used anymore:

  const clientEntryPath = _path.default.join(redwoodPaths.web.src, 'entry-client.jsx');
  return [{
    name: 'redwood-plugin-vite',
    // Used by Vite during dev, to inject the entrypoint.
    transformIndexHtml: html => {
      if (true){//(0, _fs.existsSync)(clientEntryPath)) {
        return html.replace('</head>', `<script type="module" src="entry-client.js"></script>

So the path looks same as with rollup.

But then I am getting different errors:

Warning: Expected server HTML to contain a matching <main> in <div>.
    at main
    at SplashPage (http://localhost:8910/@fs/C:/......../web/node_modules/.vite/deps/@redwoodjs_router.js?v=1b03beb6:4218:9)

Last error happens, too, when using WSL, but in WSL i do not need to change the node_module part.
Maybe worth noting that I tried this with a nearly fresh redwood project - used yarn rw -t rc

This last part seems to be related to hydration. entry-client.jsx is choosing to use react hydration instead of normal render. But it is weird, since it is no different to the index.js of webpack.

const rootElement = document.getElementById('redwood-app')
if (rootElement.children?.length > 0) {

These are the children of my redwood-app element:

1. 0: script#chakra-script
3. 2: div#redwood-announcer
4. 3: span#__chakra_env

Thanks @dennemark!

1. Windows path issues
I’ll investigate this and get a patch out ASAP

2. Hydration warnings
I’ve seen this too on @KrisCoulson’s project… and am very confused too. Observed that the exact same tree doesn’t have an error in Webpack. I’m not sure how to debug this, but would appreciate if you had any ideas!



:sparkles: Thanks @danny

Works like charm and is super fast.

Only issue I found is that vite seems to ignore the --no-open flag yarn rw dev --fwd="--no-open".

Short update 12h later: I see the same warning as @dennemark and I can work around it by changing the entry_client.jsx to


Hey @tilmannb - thanks for trying this out!

This change will get rid of the warning, but if you prerender your pages, I think it’ll cause a re-render (i.e. white flash) after the page loads, in production.

Digging into this today, I’m convinced its something to do with how quickly Vite loads, vs Webpack.

thx for the fix! going to try to create a reproduction. am literally hand-icapped right now, so need some time :slight_smile:

For reproduction I just created a new rw project and followed the vite install steps. On yarn rw dev I get following console output: Warning: Expected server HTML to contain a matching <main> in <div>. So hydration seems to be chosen as renderer. Not happening when I choose webpack.

Also tried to import entry-client.jsx at end of body. Didnt make a difference.

Worth mentioning is the existence of server-markup as a child of root-element. So vite-plugin-html already inserted it. Maybe thats the reason. But as mentioned in my first post, also chakra-ui dom elements would appear when being used in rw. Not sure if this happens after hydration or before.

I have added the issue to discord vite-feedback to seperate concerns. als found a new one. lets continue there concerning these topics?

Pretty smooth sail for now.
Just integrated GitHub - fi3ework/vite-plugin-checker: 💬 Vite plugin that provide checks of TypeScript, ESLint, vue-tsc, Stylelint and more. and it works as expected!


Started a new project and got this

gen | Generating TypeScript definitions and GraphQL schemas...
web |   ➜  Local:   http://localhost:8910/
gen | 14 files generated
api | Building... Took 160 ms
api | Debugger listening on ws://
api | For help, see: https://nodejs.org/en/docs/inspector
web | 
web |  ERROR  10:58:44 AM [vite] http proxy error at /graphql:
web | Error: connect ECONNREFUSED ::1:8911
web |     at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1247:16)
web |*
api | Starting API Server...
api | Loading server config from /Users/hakontro/code/sol/api/server.config.js 
api | 
api | 10:58:44 🌲 Configuring api side 
api | 🗒 Custom
api | {
api |   "options": {
api |     "side": "api",
api |     "_": [
api |       "api"
api |     ],
api |     "port": 8911,
api |     "p": 8911,
api |     "apiRootPath": "/",
api |     "rootPath": "/",
api |     "root-path": "/",
api |     "api-root-path": "/",
api |     "$0": "node_modules/@redwoodjs/api-server/dist/index.js"
api |   }
api | }
api | Importing Server Functions... 
api | /graphql 200 ms
api | ...Done importing in 201 ms
api | Took 222 ms
api | API listening on http://localhost:8911/
api | GraphQL endpoint at /graphql
api | 10:58:44 🐛 Fastify server configuration 
api | 🗒 Custom
api | {
api |   "requestTimeout": 15000,
api |   "connectionTimeout": 0,
api |   "keepAliveTimeout": 72000,
api |   "maxRequestsPerSocket": 0,
api |   "requestIdHeader": "request-id",
api |   "requestIdLogLabel": "reqId",
api |   "disableRequestLogging": false,
api |   "bodyLimit": 1048576,
api |   "caseSensitive": true,
api |   "allowUnsafeRegex": false,
api |   "ignoreTrailingSlash": false,
api |   "ignoreDuplicateSlashes": false,
api |   "jsonShorthand": true,
api |   "maxParamLength": 100,
api |   "onProtoPoisoning": "error",
api |   "onConstructorPoisoning": "error",
api |   "pluginTimeout": 10000,
api |   "http2SessionTimeout": 72000,
api |   "exposeHeadRoutes": true
api | }
api | 10:58:44 🐛 Registered plugins 
api | bound root 218 ms
api | ├── bound _after 3 ms
api | ├── @fastify/url-data 1 ms
api | ├── fastify-raw-body 0 ms
api | ├── bound _after 0 ms
api | ├── bound _after 0 ms
api | ├── bound _after 0 ms
api | ├── bound _after 0 ms
api | └── bound _after 0 ms
api | 
api | 10:58:44 🌲 Server listening at http://[::]:8911

seems to work though!

Thanks for this! Works great locally :slight_smile:

When I attempted to deploy though (or yarn rw build web) I get htmlWebpackPlugin is not defined because of the line seen below in index.html when it’s attempting to pre-render

 <title><%= htmlWebpackPlugin.options.title %></title>

The line seems to be the default upon installation. I simply swapped it out for an actual hardcoded title and everything was fine - but might want to adjust that for future humans?