diff --git a/docs/future/vite.md b/docs/future/vite.md
index 7b19c5b15fb..6601c1dcf8c 100644
--- a/docs/future/vite.md
+++ b/docs/future/vite.md
@@ -27,9 +27,9 @@ To get started with Vite in an existing Remix project (or a new one created with
npm install -D vite
```
-Then add `vite.config.mjs` to the project root, providing the Remix plugin to the `plugins` array:
+Then add `vite.config.ts` to the project root, providing the Remix plugin to the `plugins` array:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
@@ -52,7 +52,7 @@ The Vite plugin accepts the following subset of Remix config options:
For example:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
@@ -83,10 +83,51 @@ vite build && vite build --ssr
Since Vite is now responsible for bundling your app, there are some differences between Vite and the Remix compiler that you'll need to be aware of.
+### `` before ``
+
+During initial unstable release, the Remix Vite plugin assumes that `` component comes _before_ `` so that React Fast Refresh initialization from `` happens first.
+If `` comes before ``, [React Fast Refresh will not be able to perform HMR][rfr-preamble].
+
+```diff
+// app/root.tsx
+
+export default function App() {
+ return (
+
+
+
+
+
+
+
+
+
+
++
+
+-
+
+
+ );
+}
+```
+
+Before releasing as stable, we will redesign these APIs to make this ordering irrelevant.
+
### New Bundling Features
Vite has many [features][vite-features] and [plugins][vite-plugins] that are not built into the Remix compiler. Any use of these features will break backwards compatibility with the Remix compiler and should only be used if you intend to use Vite exclusively.
+### TypeScript
+
+Add `vite/client` types in a `.d.ts` file. We recommend replacing the existing `remix.env.d.ts` file with a new `env.d.ts` file:
+
+```ts
+///
+///
+///
+```
+
### Path Aliases
The Remix compiler leverages the `paths` option in your `tsconfig.json` to resolve path aliases. This is commonly used in the Remix community to define `~` as an alias for the `app` directory.
@@ -99,7 +140,7 @@ npm install -D vite-tsconfig-paths
Then add it to your Vite config:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
@@ -111,7 +152,7 @@ export default defineConfig({
Alternatively, you can define path aliases without referencing `tsconfig.json` by using Vite's [`resolve.alias`][vite-resolve-alias] option directly:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import { fileURLToPath, URL } from "node:url";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
@@ -153,7 +194,7 @@ If you're using Vite and the Remix compiler in the same project, you can enable
This option is only intended for use during the transition to Vite and will be removed in the future.
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
@@ -232,7 +273,7 @@ npm install -D @vanilla-extract/vite-plugin
Then add the plugin to your Vite config:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { vanillaExtractPlugin } from "@vanilla-extract/vite-plugin";
import { defineConfig } from "vite";
@@ -252,7 +293,7 @@ npm install -D @mdx-js/rollup
Then add the Rollup plugin to your Vite config:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import mdx from "@mdx-js/rollup";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
@@ -274,7 +315,7 @@ npm install -D remark-frontmatter remark-mdx-frontmatter
Then provide these plugins to the MDX Rollup plugin:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import mdx from "@mdx-js/rollup";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import remarkFrontmatter from "remark-frontmatter";
@@ -296,7 +337,7 @@ export default defineConfig({
In the Remix compiler, the frontmatter export was named `attributes`. This differs from the frontmatter plugin's default export name of `frontmatter`. To maintain backwards compatibility with the Remix compiler, you can override this via the `name` option:
-```js filename=vite.config.mjs
+```ts filename=vite.config.ts
import mdx from "@mdx-js/rollup";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
import remarkFrontmatter from "remark-frontmatter";
@@ -518,3 +559,4 @@ We're definitely late to the Vite party, but we're excited to be here now!
[solidstart]: https://start.solidjs.com/getting-started/what-is-solidstart
[sveltekit]: https://kit.svelte.dev/
[supported-with-some-deprecations]: #mdx
+[rfr-preamble]: https://github.com/facebook/react/issues/16604#issuecomment-528663101
diff --git a/integration/vite-build-test.ts b/integration/vite-build-test.ts
index 387c37c022f..245500697a5 100644
--- a/integration/vite-build-test.ts
+++ b/integration/vite-build-test.ts
@@ -20,7 +20,7 @@ test.describe("Vite build", () => {
throw new Error("Remix should not access remix.config.js when using Vite");
export default {};
`,
- "vite.config.mjs": js`
+ "vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
diff --git a/integration/vite-css-build-test.ts b/integration/vite-css-build-test.ts
index 08612c572f3..4aa0aeb331e 100644
--- a/integration/vite-css-build-test.ts
+++ b/integration/vite-css-build-test.ts
@@ -23,7 +23,7 @@ test.describe("Vite CSS build", () => {
throw new Error("Remix should not access remix.config.js when using Vite");
export default {};
`,
- "vite.config.mjs": js`
+ "vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
diff --git a/integration/vite-css-dev-test.ts b/integration/vite-css-dev-test.ts
index ba6001bbffc..5401095de37 100644
--- a/integration/vite-css-dev-test.ts
+++ b/integration/vite-css-dev-test.ts
@@ -27,14 +27,11 @@ test.describe("Vite CSS dev", () => {
throw new Error("Remix should not access remix.config.js when using Vite");
export default {};
`,
- "vite.config.mjs": js`
+ "vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
export default defineConfig({
- optimizeDeps: {
- include: ["react", "react-dom/client"],
- },
server: {
port: ${devPort},
strictPort: true,
diff --git a/integration/vite-css-legacy-build-test.ts b/integration/vite-css-legacy-build-test.ts
index e42e8d03ba7..ab487b22fcd 100644
--- a/integration/vite-css-legacy-build-test.ts
+++ b/integration/vite-css-legacy-build-test.ts
@@ -23,7 +23,7 @@ test.describe("Vite CSS legacy imports build", () => {
throw new Error("Remix should not access remix.config.js when using Vite");
export default {};
`,
- "vite.config.mjs": js`
+ "vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
diff --git a/integration/vite-dev-test.ts b/integration/vite-dev-test.ts
index e82d08c8c8b..65060385caf 100644
--- a/integration/vite-dev-test.ts
+++ b/integration/vite-dev-test.ts
@@ -24,14 +24,11 @@ test.describe("Vite dev", () => {
throw new Error("Remix should not access remix.config.js when using Vite");
export default {};
`,
- "vite.config.mjs": js`
+ "vite.config.ts": js`
import { defineConfig } from "vite";
import { unstable_vitePlugin as remix } from "@remix-run/dev";
export default defineConfig({
- optimizeDeps: {
- include: ["react", "react-dom/client"],
- },
server: {
port: ${devPort},
strictPort: true,
diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts
index 317c8c0f242..602ad8ae039 100644
--- a/packages/remix-dev/vite/plugin.ts
+++ b/packages/remix-dev/vite/plugin.ts
@@ -404,6 +404,21 @@ export const remixVitePlugin: RemixVitePlugin = (options = {}) => {
return {
appType: "custom",
experimental: { hmrPartialAccept: true },
+ optimizeDeps: {
+ include: [
+ // pre-bundle React dependencies to avoid React duplicates,
+ // even if React dependencies are not direct dependencies
+ // https://react.dev/warnings/invalid-hook-call-warning#duplicate-react
+ "react",
+ `react/jsx-runtime`,
+ `react/jsx-dev-runtime`,
+ "react-dom/client",
+ ],
+ },
+ resolve: {
+ // https://react.dev/warnings/invalid-hook-call-warning#duplicate-react
+ dedupe: ["react", "react-dom"],
+ },
...(viteCommand === "build" && {
base: pluginConfig.publicPath,
build: {