diff --git a/.changeset/hot-dogs-applaud.md b/.changeset/hot-dogs-applaud.md
index a966a76ceb8..7e41800566a 100644
--- a/.changeset/hot-dogs-applaud.md
+++ b/.changeset/hot-dogs-applaud.md
@@ -2,9 +2,9 @@
"@remix-run/dev": patch
---
-Automatically optimize dependencies during development
+(unstable) Automatic dependency optimization
-Now Remix will tell Vite to find dependencies by crawling imports starting with your route modules.
-This should resolve most (if not all) `504 Outdated Dependency` errors that could previously break HMR.
+You can now opt-in to automatic dependency optimization during development by using the `future.unstable_optimizeDeps` future flag.
+For details, check out the docs at [`Guides` > `Dependency optimization`](https://remix.run/docs/en/main/guides/dependency-optimization).
For users who were previously working around this limiation, you no longer need to explicitly add routes to Vite's `optimizeDeps.entries` nor do you need to disable the `remix-dot-server` plugin.
diff --git a/docs/guides/dependency-optimization.md b/docs/guides/dependency-optimization.md
new file mode 100644
index 00000000000..e949149606d
--- /dev/null
+++ b/docs/guides/dependency-optimization.md
@@ -0,0 +1,64 @@
+---
+title: Dependency optimization
+---
+
+This feature only affects development. It does not impact production builds.
+
+# Dependency optimization
+
+Remix introduced automatic dependency optimization in development behind the `future.unstable_optimizeDeps` [Future Flag][future-flags].
+This allows you to opt-into this behavior which will become the default in the next major version of Remix - a.k.a. React Router v7 ([1][rr-v7], [2][rr-v7-2]).
+
+In development, Vite aims to [prebundle dependencies](https://vitejs.dev/guide/dep-pre-bundling.html) so that it can efficiently serve up those dependencies on-demand.
+To do this, Vite needs to know where to start crawling your app's module graph to look for dependencies.
+
+Previously, Remix did not inform Vite to start dependency detection at route modules nor at the client entry.
+That meant that in development, Vite would encounter new dependencies as you navigated around in your app resulting in `504 Outdated Dependency` errors.
+Consequently, the development experience could feel janky at times since those errors could cause HMR to break or link navigations to be aborted.
+Navigation could also feel sluggish as processing dependencies interactively could sometimes be slow.
+
+For more information, see [Vite's Dep Optimization Options](https://vitejs.dev/config/dep-optimization-options#dep-optimization-options).
+
+## Troubleshooting
+
+### `Failed to resolve entry for package`
+
+```txt
+✘ [ERROR] Failed to resolve entry for package "". The package may have incorrect main/module/exports specified in its package.json. [plugin vite:dep-pre-bundle]
+```
+
+This is usually caused by a misconfigured dependency.
+You use [publint](https://publint.dev/) to check if the offending package is misconfigured.
+To fix the issue, you'll need to use `npm why` or `pnpm why` to determine which of your direct dependencies to add to `optimizeDeps.exclude`.
+
+For example, let's say your app is running into this error:
+
+```txt
+✘ [ERROR] Failed to resolve entry for package "jimp". The package may have incorrect main/module/exports specified in its package.json. [plugin vite:dep-pre-bundle]
+```
+
+Sure enough, `publint` reports that the [`jimp` package is misconfigured](https://publint.dev/jimp@0.22.12).
+Then, you determine that `jimp` is an indirect dependency being pulled in by your `svg2img` direct dependency:
+
+```sh
+❯ npm why jimp
+jimp@0.16.13
+node_modules/jimp
+ jimp@"^0.16.1" from svg2img@1.0.0-beta.2
+ node_modules/svg2img
+ svg2img@"^1.0.0-beta.2" from the root project
+```
+
+Finally, you add `svg2img` to `optimizeDeps.exclude`, which should fix the issue:
+
+```ts filename=vite.config.ts
+export default defineConfig({
+ optimizeDeps: {
+ exclude: ["svg2img"],
+ },
+});
+```
+
+[future-flags]: ../guides/api-development-strategy
+[rr-v7]: https://remix.run/blog/merging-remix-and-react-router
+[rr-v7-2]: https://remix.run/blog/incremental-path-to-react-19
diff --git a/docs/start/future-flags.md b/docs/start/future-flags.md
index a262544c0a7..522b65a0820 100644
--- a/docs/start/future-flags.md
+++ b/docs/start/future-flags.md
@@ -340,6 +340,10 @@ Opt into [Single Fetch][single-fetch] behavior (details will be expanded once th
Opt into [Lazy Route Discovery][lazy-route-discovery] behavior (details will be expanded once the flag stabilizes).
+## unstable_optimizeDeps
+
+Opt into automatic [dependency optimization][dependency-optimization] during development.
+
[development-strategy]: ../guides/api-development-strategy
[fetcherpersist-rfc]: https://github.com/remix-run/remix/discussions/7698
[relativesplatpath-changelog]: https://github.com/remix-run/remix/blob/main/CHANGELOG.md#futurev3_relativesplatpath
@@ -357,3 +361,4 @@ Opt into [Lazy Route Discovery][lazy-route-discovery] behavior (details will be
[vite-url-imports]: https://vitejs.dev/guide/assets.html#explicit-url-imports
[mdx]: https://mdxjs.com
[mdx-rollup-plugin]: https://mdxjs.com/packages/rollup
+[dependency-optimization]: ../guides/dependency-optimization
diff --git a/packages/remix-dev/config.ts b/packages/remix-dev/config.ts
index 8f993d61888..7234202be8f 100644
--- a/packages/remix-dev/config.ts
+++ b/packages/remix-dev/config.ts
@@ -39,6 +39,7 @@ interface FutureConfig {
v3_throwAbortReason: boolean;
unstable_singleFetch: boolean;
unstable_lazyRouteDiscovery: boolean;
+ unstable_optimizeDeps: boolean;
}
type NodeBuiltinsPolyfillOptions = Pick<
@@ -611,6 +612,7 @@ export async function resolveConfig(
unstable_singleFetch: appConfig.future?.unstable_singleFetch === true,
unstable_lazyRouteDiscovery:
appConfig.future?.unstable_lazyRouteDiscovery === true,
+ unstable_optimizeDeps: appConfig.future?.unstable_optimizeDeps === true,
};
if (appConfig.future) {
diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts
index ca683f03538..d96d6b547cd 100644
--- a/packages/remix-dev/vite/plugin.ts
+++ b/packages/remix-dev/vite/plugin.ts
@@ -1075,12 +1075,14 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => {
: undefined,
},
optimizeDeps: {
- entries: [
- ctx.entryClientFilePath,
- ...Object.values(ctx.remixConfig.routes).map((route) =>
- path.join(ctx.remixConfig.appDirectory, route.file)
- ),
- ],
+ entries: ctx.remixConfig.future.unstable_optimizeDeps
+ ? [
+ ctx.entryClientFilePath,
+ ...Object.values(ctx.remixConfig.routes).map((route) =>
+ path.join(ctx.remixConfig.appDirectory, route.file)
+ ),
+ ]
+ : [],
include: [
// Pre-bundle React dependencies to avoid React duplicates,
// even if React dependencies are not direct dependencies.