diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91b3d330..6a0aa446 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,13 @@
# @shopify/shopify-app-template-remix
-## 2024.11.26
+## 2024.12.04
+- [#891](https://github.com/Shopify/shopify-app-template-remix/pull/891) Enable remix future flags.
+-
+## 2024.11.26
- [888](https://github.com/Shopify/shopify-app-template-remix/pull/888) Update restResources version to 2024-10
+
## 2024.11.06
- [881](https://github.com/Shopify/shopify-app-template-remix/pull/881) Update to the productCreate mutation to use the new ProductCreateInput type
diff --git a/README.md b/README.md
index 1ffdc8fa..fd1a376e 100644
--- a/README.md
+++ b/README.md
@@ -86,7 +86,7 @@ export async function loader({ request }) {
},
} = await response.json();
- return json(nodes);
+ return nodes;
}
```
diff --git a/app/entry.server.jsx b/app/entry.server.jsx
index 10e8d37d..77a0c2aa 100644
--- a/app/entry.server.jsx
+++ b/app/entry.server.jsx
@@ -5,7 +5,7 @@ import { createReadableStreamFromReadable } from "@remix-run/node";
import { isbot } from "isbot";
import { addDocumentResponseHeaders } from "./shopify.server";
-const ABORT_DELAY = 5000;
+export const streamTimeout = 5000;
export default async function handleRequest(
request,
@@ -19,11 +19,7 @@ export default async function handleRequest(
return new Promise((resolve, reject) => {
const { pipe, abort } = renderToPipeableStream(
- ,
+ ,
{
[callbackName]: () => {
const body = new PassThrough();
@@ -48,6 +44,8 @@ export default async function handleRequest(
},
);
- setTimeout(abort, ABORT_DELAY);
+ // Automatically timeout the React renderer after 6 seconds, which ensures
+ // React has enough time to flush down the rejected boundary contents
+ setTimeout(abort, streamTimeout + 1000);
});
}
diff --git a/app/routes.js b/app/routes.js
new file mode 100644
index 00000000..83892841
--- /dev/null
+++ b/app/routes.js
@@ -0,0 +1,3 @@
+import { flatRoutes } from "@remix-run/fs-routes";
+
+export default flatRoutes();
diff --git a/app/routes/_index/route.jsx b/app/routes/_index/route.jsx
index b319dbab..e86793f9 100644
--- a/app/routes/_index/route.jsx
+++ b/app/routes/_index/route.jsx
@@ -1,4 +1,4 @@
-import { json, redirect } from "@remix-run/node";
+import { redirect } from "@remix-run/node";
import { Form, useLoaderData } from "@remix-run/react";
import { login } from "../../shopify.server";
import styles from "./styles.module.css";
@@ -10,7 +10,7 @@ export const loader = async ({ request }) => {
throw redirect(`/app?${url.searchParams.toString()}`);
}
- return json({ showForm: Boolean(login) });
+ return { showForm: Boolean(login) };
};
export default function App() {
diff --git a/app/routes/app._index.jsx b/app/routes/app._index.jsx
index dae489ee..9854a9af 100644
--- a/app/routes/app._index.jsx
+++ b/app/routes/app._index.jsx
@@ -1,5 +1,4 @@
import { useEffect } from "react";
-import { json } from "@remix-run/node";
import { useFetcher } from "@remix-run/react";
import {
Page,
@@ -81,10 +80,10 @@ export const action = async ({ request }) => {
);
const variantResponseJson = await variantResponse.json();
- return json({
+ return {
product: responseJson.data.productCreate.product,
variant: variantResponseJson.data.productVariantsBulkUpdate.productVariants,
- });
+ };
};
export default function Index() {
diff --git a/app/routes/app.jsx b/app/routes/app.jsx
index 65293cdd..4bf86906 100644
--- a/app/routes/app.jsx
+++ b/app/routes/app.jsx
@@ -1,4 +1,3 @@
-import { json } from "@remix-run/node";
import { Link, Outlet, useLoaderData, useRouteError } from "@remix-run/react";
import { boundary } from "@shopify/shopify-app-remix/server";
import { AppProvider } from "@shopify/shopify-app-remix/react";
@@ -11,7 +10,7 @@ export const links = () => [{ rel: "stylesheet", href: polarisStyles }];
export const loader = async ({ request }) => {
await authenticate.admin(request);
- return json({ apiKey: process.env.SHOPIFY_API_KEY || "" });
+ return { apiKey: process.env.SHOPIFY_API_KEY || "" };
};
export default function App() {
diff --git a/app/routes/auth.login/route.jsx b/app/routes/auth.login/route.jsx
index 73b477ae..5e19ff71 100644
--- a/app/routes/auth.login/route.jsx
+++ b/app/routes/auth.login/route.jsx
@@ -1,5 +1,4 @@
import { useState } from "react";
-import { json } from "@remix-run/node";
import { Form, useActionData, useLoaderData } from "@remix-run/react";
import {
AppProvider as PolarisAppProvider,
@@ -20,15 +19,15 @@ export const links = () => [{ rel: "stylesheet", href: polarisStyles }];
export const loader = async ({ request }) => {
const errors = loginErrorMessage(await login(request));
- return json({ errors, polarisTranslations });
+ return { errors, polarisTranslations };
};
export const action = async ({ request }) => {
const errors = loginErrorMessage(await login(request));
- return json({
+ return {
errors,
- });
+ };
};
export default function Auth() {
diff --git a/package.json b/package.json
index 7d2409da..20f843da 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,8 @@
},
"devDependencies": {
"@remix-run/eslint-config": "^2.7.1",
+ "@remix-run/fs-routes": "^2.15.0",
+ "@remix-run/route-config": "^2.15.0",
"@shopify/api-codegen-preset": "^1.1.1",
"@types/eslint": "^8.40.0",
"@types/node": "^22.2.0",
diff --git a/vite.config.js b/vite.config.js
index 5003fa44..204a574d 100644
--- a/vite.config.js
+++ b/vite.config.js
@@ -1,7 +1,10 @@
import { vitePlugin as remix } from "@remix-run/dev";
+import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
+installGlobals({ nativeFetch: true });
+
// Related: https://github.com/remix-run/remix/issues/2835#issuecomment-1144102176
// Replace the HOST env var with SHOPIFY_APP_URL so that it doesn't break the remix server. The CLI will eventually
// stop passing in HOST, so we can remove this workaround after the next major release.
@@ -46,6 +49,14 @@ export default defineConfig({
plugins: [
remix({
ignoredRouteFiles: ["**/.*"],
+ future: {
+ v3_fetcherPersist: true,
+ v3_relativeSplatPath: true,
+ v3_throwAbortReason: true,
+ v3_lazyRouteDiscovery: true,
+ v3_singleFetch: true,
+ v3_routeConfig: true,
+ },
}),
tsconfigPaths(),
],