Skip to content

Migrate your Remix app to Vite

Paulo Margarido edited this page Apr 18, 2024 · 6 revisions

To improve and extend Remix's bundling capabilities, Vite has been added to Remix as an alternative bundler. In the future, Vite will become the default bundler for Remix.

Beginning in late February 2024, Shopify CLI uses Vite in its Remix app template, and all new apps now use Vite by default.

If you have a Remix app and want to migrate to Vite, then you can follow the instructions in this page.

Prerequisites

Your app was created before February 21, 2024.

Any new apps created after that date already use Vite.

Update the app dependencies

To use Vite in your app, you need to add some additional dependencies, and update some Remix dependencies.

  1. Run one of the following commands to install the dependencies for Vite:

    npm

    npm add --save-dev vite
    npm add vite-tsconfig-paths

    yarn

    yarn add --dev vite
    yarn add vite-tsconfig-paths

    pnpm

    pnpm add --save-dev vite
    pnpm add vite-tsconfig-paths
  2. Make sure that all of your @remix-run dependencies are at least 2.7.0, and set the type setting to module so it works with "moduleResolution": "Bundler":

    /package.json

      "dependencies": {
    -   "@remix-run/dev": "^2.0.0",
    -   "@remix-run/node": "^2.0.0",
    -   "@remix-run/react": "^2.0.0",
    -   "@remix-run/serve": "^2.0.0",
    +   "@remix-run/dev": "^2.7.0",
    +   "@remix-run/node": "^2.7.0",
    +   "@remix-run/react": "^2.7.0",
    +   "@remix-run/serve": "^2.7.0",
      },
      "devDependencies": {
    -   "@remix-run/eslint-config": "^2.0.0",
    +   "@remix-run/eslint-config": "^2.7.0",
      },

Update scripts

Because the app now runs on Vite scripts, you need to update the scripts that are used to run or build your app.

  1. Update your script aliases in package.json:

      "scripts": {
    -   "build": "tsc && remix build",
    -   "start": "remix-serve build/index.js",
    +   "build": "vite build && vite build --ssr",
    +   "start": "remix-serve ./build/server/index.js",
      },
  2. Shopify CLI runs the dev script, so you need to update it in shopify.web.toml:

      [commands]
    - dev = "npx prisma generate && npx prisma migrate deploy && npm exec remix dev"
    + dev = "npx prisma generate && npx prisma migrate deploy && npm exec remix vite:dev"
    
    - [hmr_server]
    - http_paths = ["/ping"]

Update configuration files

When using Vite, the way you can set Remix configurations differs slightly.

  1. Remix no longer uses its own configuration file, so you need to delete remix.config.js, and add a new vite.config.[ts|js] file.

    This file will use the Vite plugin for Remix, and set your server up so that Hot Module Replacement (HMR) will work when running the app using Shopify CLI.

    TypeScript (/vite.config.ts)
    import { vitePlugin as remix } from "@remix-run/dev";
    import { defineConfig, type UserConfig } from "vite";
    import tsconfigPaths from "vite-tsconfig-paths";
    
    if (
      process.env.HOST &&
      (!process.env.SHOPIFY_APP_URL ||
        process.env.SHOPIFY_APP_URL === process.env.HOST)
    ) {
      process.env.SHOPIFY_APP_URL = process.env.HOST;
      delete process.env.HOST;
    }
    
    const host = new URL(process.env.SHOPIFY_APP_URL || "http://localhost")
      .hostname;
    
    let hmrConfig;
    if (host === "localhost") {
      hmrConfig = {
        protocol: "ws",
        host: "localhost",
        port: 64999,
        clientPort: 64999,
      };
    } else {
      hmrConfig = {
        protocol: "wss",
        host: host,
        port: parseInt(process.env.FRONTEND_PORT!) || 8002,
        clientPort: 443,
      };
    }
    
    export default defineConfig({
      server: {
        port: Number(process.env.PORT || 3000),
        hmr: hmrConfig,
        fs: {
          // See https://vitejs.dev/config/server-options.html#server-fs-allow for more information
          allow: ["app", "node_modules"],
        },
      },
      plugins: [
        remix({
          ignoredRouteFiles: ["**/.*"],
        }),
        tsconfigPaths(),
      ],
      build: {
        assetsInlineLimit: 0,
      },
    }) satisfies UserConfig;
    JavaScript (/vite.config.js)
    import { vitePlugin as remix } from "@remix-run/dev";
    import { defineConfig } from "vite";
    import tsconfigPaths from "vite-tsconfig-paths";
    
    if (
      process.env.HOST &&
      (!process.env.SHOPIFY_APP_URL ||
        process.env.SHOPIFY_APP_URL === process.env.HOST)
    ) {
      process.env.SHOPIFY_APP_URL = process.env.HOST;
      delete process.env.HOST;
    }
    
    const host = new URL(process.env.SHOPIFY_APP_URL || "http://localhost")
      .hostname;
    
    let hmrConfig;
    if (host === "localhost") {
      hmrConfig = {
        protocol: "ws",
        host: "localhost",
        port: 64999,
        clientPort: 64999,
      };
    } else {
      hmrConfig = {
        protocol: "wss",
        host: host,
        port: parseInt(process.env.FRONTEND_PORT!) || 8002,
        clientPort: 443,
      };
    }
    
    export default defineConfig({
      server: {
        port: Number(process.env.PORT || 3000),
        hmr: hmrConfig,
        fs: {
          // See https://vitejs.dev/config/server-options.html#server-fs-allow for more information
          allow: ["app", "node_modules"],
        },
      },
      plugins: [
        remix({
          ignoredRouteFiles: ["**/.*"],
        }),
        tsconfigPaths(),
      ],
      build: {
        assetsInlineLimit: 0,
      },
    });

Tip

If you're not using TypeScript, you can skip steps 2 and 3.

  1. Configure the appropriate TypeScript types. To do that, create a new file called env.d.ts, with the following contents:

    /// <reference types="vite/client" />
    /// <reference types="@remix-run/node" />
  2. Update your tsconfig.json file to include the correct libraries and module settings:

    - "include": ["**/*.[ts|js]", "**/*.[tsx|jsx]"],
    + "include": ["env.d.ts", "**/*.ts", "**/*.tsx"],
      "compilerOptions": {
    -   "lib": ["DOM", "DOM.Iterable", "ES2019"],
    -   "module": "Node16",
    -   "moduleResolution": "Node16",
    -   "target": "ES2019",
    +   "lib": ["DOM", "DOM.Iterable", "ES2022"],
    +   "module": "ESNext",
    +   "moduleResolution": "Bundler",
    +   "target": "ES2022",
      }

Update route files

After your app is fully configured, you can start updating your routes.

The main changes are that the <LiveReload /> component is no longer required, and you can import CSS files directly as modules, instead of using the links export.

  1. Remove <LiveReload /> from /app/root.[tsx|jsx]:

      import {
    -   LiveReload,
        Scripts,
      } from "@remix-run/react";
    
      export default function App() {
        return (
          <html>
            <body>
    -         <LiveReload />
              <Scripts />
            </body>
          </html>
        );
      }
  2. Only use the links export in your routes when you're importing global CSS, and add the ?url flag when using links. Use CSS module imports for app-specific CSS files.

    The following exports were included in previous versions of the template. You might need to remove exports from other routes in your app.

    /app/routes/_index/route.[tsx|jsx]:

    - import indexStyles from "./style.css";
    + import "./styles.css";
    - export const links = () => [{ rel: "stylesheet", href: indexStyles }];

    /app/routes/app.[tsx|jsx]:

    - import polarisStyles from "@shopify/polaris/build/esm/styles.css";
    + import polarisStyles from "@shopify/polaris/build/esm/styles.css?url";
    export const links = () => [{ rel: "stylesheet", href: polarisStyles }];

    /app/routes/auth.login/route.[tsx|jsx]:

    - import polarisStyles from "@shopify/polaris/build/esm/styles.css";
    + import polarisStyles from "@shopify/polaris/build/esm/styles.css?url";
    export const links = () => [{ rel: "stylesheet", href: polarisStyles }];
  3. Because the app is using the Bundler moduleResolution value, you can no longer use require() statements. Because of this, you need to update the import for Polaris translations in /app/routes/auth.login/route.[tsx|jsx].

    + import polarisTranslations from "@shopify/polaris/locales/en.json";
    
      export const loader = async ({ request }: LoaderFunctionArgs) => {
    -   return json({
    -     errors,
    -     polarisTranslations: require(`@shopify/polaris/locales/en.json`),
    -   });
    +   return json({ errors, polarisTranslations });
      };

Make additional changes as required

This tutorial covers the steps required to migrate an app that has not been modified from the original Remix template to Vite. Depending on your app's features and functionality, you might need to make additional changes to complete the migration. Review the Remix migration instructions to understand whether there are other changes that you need to make.

Test your app

After making all of the changes, you should be able to run or build your app as before.