Migrating a Flow typed React SPA project from create-react-app (and Webpack) to Vite

I had a project based on Create React App, using Flow types, which I wanted to migrate to Vite. These are roughly the steps I had to follow:

  1. Most of my JSX files had a .js file extension. I renamed them all to .jsx – Vite is much happier that way
  2. I installed Vite and a few plugins:
    • @bunchtogether/vite-plugin-flow (to allow working with Flow types)
    • @vitejs/plugin-react
    • vite-jsconfig-paths
    • @vitejs/plugin-react-refresh
    • vite-plugin-svgr
yarn install -D vite vite-jsconfig-paths vite-plugin-svgr @vitejs/plugin-react @vitejs/plugin-react-refresh @bunchtogether/vite-plugin-flow
  1. Added a vite.config.js to the project root:
/* eslint-disable flowtype/require-valid-file-annotation */

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgrPlugin from "vite-plugin-svgr";
import { flowPlugin, esbuildFlowPlugin } from "@bunchtogether/vite-plugin-flow";
import jsconfigPaths from "vite-jsconfig-paths";
import * as path from "path";

// https://vitejs.dev/config/
/** @type {import('vite').UserConfig} */
export default defineConfig({
  optimizeDeps: {
    esbuildOptions: {
      plugins: [esbuildFlowPlugin()],
    },
  },
  plugins: [
    flowPlugin(), react(), jsconfigPaths(), svgrPlugin()
  ],
  resolve: {
    alias: [
      { find: "styles", replacement: path.resolve(__dirname, "src/styles") },
    ],
  },
});
  1. Added a new file at /flow-typed/vite_import_meta.js to handle Vite’s handling of environment variables:
// @flow
declare interface Import$Meta extends Import$Meta {
  env: {
    [key: string]: string,
  };
}
  1. Update the paths in my .jsconfig file:
{
  "compilerOptions": {
    "baseUrl": "src",
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "paths": {
      "flow-types": ["flow-types.js"],
      "polyfills": ["polyfills.js"],
      "apis/": ["apis/"],
      "App/": ["App/"],
      "assets/": ["assets/"],
      "constants/": ["constants/"],
      "errors/": ["errors/"],
      "hoc/": ["hoc/"],
      "hooks/": ["hooks/"],
      "redux/": ["redux/"],
      "styles/": ["styles/"],
      "utils/": ["utils/"],
      "validators/": ["validators/"]
    }
  },
  "include": ["src"],
  "exclude": [
    "node_modules",
    ".storybook",
    "Auth0",
    "flow-typed",
    "dist",
    ".idea"
  ]
}
  1. Change the prefix on all .env variables from REACT_APP_ to VITE_
  2. [ ] Change all instances of process.env["REACT_APP_WHATEVER"] to import.meta.env.VITE_WHATEVER
  3. Move index.html from public to the project root, and update any URLs to use %VITE_WHATEVER%
  4. Add this to the ignore section of your .flowconfig:
    /vite.config.js
  5. I ran into issues with circular references. I was able to find them with: npx madge src/index.jsx --circular
  6. Test like hell!

Thanks to Cathal Mac Donnacha, whose post about migrating a typescript project from CRA to Vite was hugely helpful.

Update: I’ve since migrated the project to use Flow’s Hermes parser.