No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.17.1.
-
Add
errorHeaders
parameter to the leafheaders()
function to expose headers from thrown responses that bubble up to ancestor route boundaries. If the throwing route contains the boundary, thenerrorHeaders
will be the same object asloaderHeaders
/actionHeaders
for that route. (#6425, #6475) -
Add optional
handleError
export for custom server-side error processing. This is a new optional export from yourentry.server.tsx
that will be called with any encountered error on the Remix server (loader, action, or render error) (#6495, #6524):// entry.server.tsx export function handleError( error: unknown, { request, params, context }: DataFunctionArgs ): void { if (error instanceof Error) { sendErrorToBugReportingService(error); console.error(formatError(error)); } else { const unknownError = new Error("Unknown Server Error"); sendErrorToBugReportingService(unknownError); console.error(unknownError); } }
-
Force Typescript to simplify type produced by
Serialize
. (#6449)As a result, the following types and functions have simplified return types:
- SerializeFrom
- useLoaderData
- useActionData
- useFetcher
type Data = { hello: string; when: Date }; // BEFORE type Unsimplified = SerializeFrom<Data>; // ^? SerializeObject<UndefinedToOptional<{ hello: string; when: Date }>> // AFTER type Simplified = SerializeFrom<Data>; // ^? { hello: string; when: string }
-
Added a new
future.v2_headers
future flag to opt into automatic inheriting of ancestor routeheaders
functions so you do not need to export aheaders
function from every possible leaf route if you don't wish to. (#6431) -
Reuse dev server port for WebSocket (Live Reload,HMR,HDR) (#6476)
As a result the
webSocketPort
/--websocket-port
option has been obsoleted. Additionally, scheme/host/port options for the dev server have been renamed.Available options are:
Option flag config default Command -c
/--command
command
remix-serve <server build path>
Scheme --scheme
scheme
http
Host --host
host
localhost
Port --port
port
Dynamically chosen open port No restart --no-restart
restart: false
restart: true
Note that scheme/host/port options are for the dev server, not your app server. You probably don't need to use scheme/host/port option if you aren't configuring networking (e.g. for Docker or SSL).
-
Properly handle thrown
ErrorResponse
instances inside resource routes (#6320) -
Add
HeadersArgs
type to be consistent with loaders/actions/meta and allows for using afunction
declaration in addition to an arrow function expression (#6247)import type { HeadersArgs } from "@remix-run/node"; // or cloudflare/deno export function headers({ loaderHeaders }: HeadersArgs) { return { "x-my-custom-thing": loaderHeaders.get("x-my-custom-thing") || "fallback", }; }
-
Ensure un-sanitized server errors are logged on the server during document requests (#6495)
-
Updated dependencies:
- Expose methods in the
SessionStorage
interface as arrow functions so destructuring is correctly part of the contract. (#6330) - Fix
data
parameter typing onV2_MetaFunction
to includeundefined
for scenarios in which theloader
threw to it's own boundary. (#6231) - Updated dependencies:
-
Enable support for CSS Modules, Vanilla Extract and CSS side-effect imports (#6046)
These CSS bundling features were previously only available via
future.unstable_cssModules
,future.unstable_vanillaExtract
andfuture.unstable_cssSideEffectImports
options inremix.config.js
, but they have now been stabilized.In order to use these features, check out our guide to CSS bundling in your project.
-
Stabilize built-in PostCSS support via the new
postcss
option inremix.config.js
. As a result, thefuture.unstable_postcss
option has also been deprecated. (#5960)The
postcss
option isfalse
by default, but when set totrue
will enable processing of all CSS files using PostCSS ifpostcss.config.js
is present.If you followed the original PostCSS setup guide for Remix, you may have a folder structure that looks like this, separating your source files from its processed output:
. ├── app │ └── styles (processed files) │ ├── app.css │ └── routes │ └── index.css └── styles (source files) ├── app.css └── routes └── index.css
After you've enabled the new
postcss
option, you can delete the processed files fromapp/styles
folder and move your source files fromstyles
toapp/styles
:. ├── app │ └── styles (source files) │ ├── app.css │ └── routes │ └── index.css
You should then remove
app/styles
from your.gitignore
file since it now contains source files rather than processed output.You can then update your
package.json
scripts to remove any usage ofpostcss
since Remix handles this automatically. For example, if you had followed the original setup guide:{ "scripts": { - "dev:css": "postcss styles --base styles --dir app/styles -w", - "build:css": "postcss styles --base styles --dir app/styles --env production", - "dev": "concurrently \"npm run dev:css\" \"remix dev\"" + "dev": "remix dev" } }
-
Stabilize built-in Tailwind support via the new
tailwind
option inremix.config.js
. As a result, thefuture.unstable_tailwind
option has also been deprecated. (#5960)The
tailwind
option isfalse
by default, but when set totrue
will enable built-in support for Tailwind functions and directives in your CSS files iftailwindcss
is installed.If you followed the original Tailwind setup guide for Remix and want to make use of this feature, you should first delete the generated
app/tailwind.css
.Then, if you have a
styles/tailwind.css
file, you should move it toapp/tailwind.css
.rm app/tailwind.css mv styles/tailwind.css app/tailwind.css
Otherwise, if you don't already have an
app/tailwind.css
file, you should create one with the following contents:@tailwind base; @tailwind components; @tailwind utilities;
You should then remove
/app/tailwind.css
from your.gitignore
file since it now contains source code rather than processed output.You can then update your
package.json
scripts to remove any usage oftailwindcss
since Remix handles this automatically. For example, if you had followed the original setup guide:{ // ... "scripts": { - "build": "run-s \"build:*\"", + "build": "remix build", - "build:css": "npm run generate:css -- --minify", - "build:remix": "remix build", - "dev": "run-p \"dev:*\"", + "dev": "remix dev", - "dev:css": "npm run generate:css -- --watch", - "dev:remix": "remix dev", - "generate:css": "npx tailwindcss -o ./app/tailwind.css", "start": "remix-serve build" } // ... }
-
The Remix dev server spins up your app server as a managed subprocess. (#6133) This keeps your development environment as close to production as possible. It also means that the Remix dev server is compatible with any app server.
By default, the dev server will use the Remix App Server, but you opt to use your own app server by specifying the command to run it via the
-c
/--command
flag:remix dev # uses `remix-serve <serve build path>` as the app server remix dev -c "node ./server.js" # uses your custom app server at `./server.js`
The dev server will:
- force
NODE_ENV=development
and warn you if it was previously set to something else - rebuild your app whenever your Remix app code changes
- restart your app server whenever rebuilds succeed
- handle live reload and HMR + Hot Data Revalidation
In order to manage your app server, the dev server needs to be told what server build is currently being used by your app server. This works by having the app server send a "I'm ready!" message with the Remix server build hash as the payload.
This is handled automatically in Remix App Server and is set up for you via calls to
broadcastDevReady
orlogDevReady
in the official Remix templates.If you are not using Remix App Server and your server doesn't call
broadcastDevReady
, you'll need to call it in your app server after it is up and running. For example, in an Express server:// server.js // <other imports> import { broadcastDevReady } from "@remix-run/node"; // Path to Remix's server build directory ('build/' by default) const BUILD_DIR = path.join(process.cwd(), "build"); // <code setting up your express server> app.listen(3000, () => { const build = require(BUILD_DIR); console.log("Ready: http://localhost:" + port); // in development, call `broadcastDevReady` _after_ your server is up and running if (process.env.NODE_ENV === "development") { broadcastDevReady(build); } });
Options priority order is: 1. flags, 2. config, 3. defaults.
Option flag config default Command -c
/--command
command
remix-serve <server build path>
HTTP(S) scheme --http-scheme
httpScheme
http
HTTP(S) host --http-host
httpHost
localhost
HTTP(S) port --http-port
httpPort
Dynamically chosen open port Websocket port --websocket-port
websocketPort
Dynamically chosen open port No restart --no-restart
restart: false
restart: true
🚨 The
--http-*
flags are only used for internal dev server <-> app server communication. Your app will run on your app server's normal URL.To set
unstable_dev
configuration, replaceunstable_dev: true
withunstable_dev: { <options> }
. For example, to set the HTTP(S) port statically:// remix.config.js module.exports = { future: { unstable_dev: { httpPort: 8001, }, }, };
You should only need to use the
--http-*
flags and--websocket-port
flag if you need fine-grain control of what scheme/host/port for the dev server. If you are setting up SSL or Docker networking, these are the flags you'll want to use.🚨 Remix will not set up SSL and custom host for you. The
--http-scheme
and--http-host
flag are for you to tell Remix how you've set things up. It is your task to set up SSL certificates and host files if you want those features.If you want to manage server changes yourself, you can use the
--no-restart
flag to tell the dev server to refrain from restarting your app server when builds succeed:remix dev -c "node ./server.js" --no-restart
For example, you could purge the
require
cache of your app server to keep it running while picking up server changes. If you do so, you should watch the server build path (build/
by default) for changes and only purge therequire
cache when changes are detected.🚨 If you use
--no-restart
, it is your responsibility to callbroadcastDevReady
when your app server has picked up server changes. For example, withchokidar
:// server.dev.js // eslint-disable-next-line no-restricted-globals const BUILD_PATH = path.resolve(__dirname, "build"); const watcher = chokidar.watch(BUILD_PATH); watcher.on("change", () => { // 1. purge require cache purgeRequireCache(); // 2. load updated server build const build = require(BUILD_PATH); // 3. tell dev server that this app server is now ready broadcastDevReady(build); });
- force
- add
logDevReady
as replacement for platforms that can't initialize async I/O outside of the request response lifecycle. (#6204) - better type discrimination when unwrapping loader return types (#5516)
- pass
AppLoadContext
tohandleRequest
(#5836) - Updated dependencies:
-
We have made a few changes to the API for route module
meta
functions when using thefuture.v2_meta
flag. These changes are only breaking for users who have opted in. (#5746)V2_HtmlMetaDescriptor
has been renamed toV2_MetaDescriptor
- The
meta
function's arguments have been simplifiedparentsData
has been removed, as each route's loader data is available on thedata
property of its respectivematch
object// before export function meta({ parentsData }) { return [{ title: parentsData["routes/some-route"].title }]; } // after export function meta({ matches }) { return [ { title: matches.find((match) => match.id === "routes/some-route") .data.title, }, ]; }
- The
route
property on route matches has been removed, as relevant match data is attached directly to the match object// before export function meta({ matches }) { const rootModule = matches.find((match) => match.route.id === "root"); } // after export function meta({ matches }) { const rootModule = matches.find((match) => match.id === "root"); }
- Added support for generating
<script type='application/ld+json' />
and meta-related<link />
tags to document head via the routemeta
function when using thev2_meta
future flag
-
Added a new
future.v2_normalizeFormMethod
flag to normalize the exposeduseNavigation().formMethod
as an uppercase HTTP method to align with the previoususeTransition
behavior as well as thefetch()
behavior of normalizing to uppercase HTTP methods. (#5815)- When
future.v2_normalizeFormMethod === false
,useNavigation().formMethod
is lowercaseuseFetcher().formMethod
is uppercase
- When
future.v2_normalizeFormMethod === true
:useNavigation().formMethod
is uppercaseuseFetcher().formMethod
is uppercase
- When
-
Added deprecation warning for
CatchBoundary
in favor offuture.v2_errorBoundary
(#5718) -
Added experimental support for Vanilla Extract caching, which can be enabled by setting
future.unstable_vanillaExtract: { cache: true }
inremix.config
. This is considered experimental due to the use of a brand new Vanilla Extract compiler under the hood. In order to use this feature, you must be using at leastv1.10.0
of@vanilla-extract/css
. (#5735)
- Bumped React Router dependencies to the latest version. See the release notes for more details. (
e14699547
) - Added type deprecations for types now in React Router (#5679)
- Stopped logging server errors for aborted requests (#5602)
- We now ensure that stack traces are removed from all server side errors in production (#5541)
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.14.3.
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.14.2.
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.14.1.
- Hot Module Replacement and Hot Data Revalidation (#5259)
- Requires
unstable_dev
future flag to be enabled - HMR provided through React Refresh
- Features:
- HMR for component and style changes
- HDR when loaders for current route change
- Known limitations for MVP:
- Only implemented for React via React Refresh
- No
import.meta.hot
API exposed yet - Revalidates all loaders on route when loader changes are detected
- Loader changes do not account for imported dependencies changing
- Requires
- Sync
FutureConfig
interface between packages (#5398) - Updated dependencies:
@remix-run/[email protected]
[email protected]
- Add built-in support for PostCSS via the
future.unstable_postcss
feature flag (#5229) - Add built-in support for Tailwind via the
future.unstable_tailwind
feature flag (#5229)
- Bump React Router dependencies to the latest version. See the release notes for more details. (#5389)
- Improve efficiency of route manifest-to-tree transformation (#4748)
- Added a new development server available in the Remix config under the
unstable_dev
flag. See the release notes for a full description. (#5133) - Removed
react
&react-dom
frompeerDependencies
(#4801)
- Bump React Router dependencies to the latest version. See the release notes for more details. (#5242)
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.11.1.
- Added support for Vanilla Extract via the
unstable_vanillaExtract
future flag. IMPORTANT: Features marked withunstable
are … unstable. While we're confident in the use cases they solve, the API and implementation may change without a major version bump. (#5040) - Add support for CSS side-effect imports via the
unstable_cssSideEffectImports
future flag. IMPORTANT: Features marked withunstable
are … unstable. While we're confident in the use cases they solve, the API and implementation may change without a major version bump. (#4919) - Add support for CSS Modules via the
unstable_cssModules
future flag. IMPORTANT: Features marked withunstable
are … unstable. While we're confident in the use cases they solve, the API and implementation may change without a major version bump. (#4852)
-
Added the
v2_errorBoundary
future flag to opt into the next version of Remix'sErrorBoundary
behavior. This removes the separateCatchBoundary
andErrorBoundary
and consolidates them into a singleErrorBoundary
, following the logic used byerrorElement
in React Router. You can then useisRouteErrorResponse
to differentiate between thrownResponse
/Error
instances. (#4918)// Current (Remix v1 default) import { useCatch } from "@remix-run/react"; export function CatchBoundary() { const caught = useCatch(); return ( <p> {caught.status} {caught.data} </p> ); } export function ErrorBoundary({ error }) { return <p>{error.message}</p>; }
// Using future.v2_errorBoundary import { isRouteErrorResponse, useRouteError } from "@remix-run/react"; export function ErrorBoundary() { const error = useRouteError(); return isRouteErrorResponse(error) ? ( <p> {error.status} {error.data} </p> ) : ( <p>{error.message}</p> ); }
-
Introduces the
defer()
API from@remix-run/router
with support for server-rendering and HTTP streaming. This utility allows you to defer values returned fromloader
functions by returning promises instead of resolved values. This has been refered to as "sending a promise over the wire". (#4920)Informational Resources:
- https://gist.github.com/jacob-ebey/9bde9546c1aafaa6bc8c242054b1be26
- https://github.com/remix-run/remix/blob/main/decisions/0004-streaming-apis.md
Documentation Resources (better docs specific to Remix are in the works):
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.10.1.
- Update Remix to use new data APIs introduced in React Router v6.4 (#4900)
- Export
V2_HtmlMetaDescriptor
andV2_MetaFunction
types from runtime packages (#4943) - Fix
V2_MetaFunction
to returnV2_HtmlMetaDescriptor[]
type (#4947)
- Fix
TypedResponse
so that Typescript correctly shows errors for incompatible types inloader
andaction
functions. (#4734) - Fix error boundary tracking for multiple errors bubbling to the same boundary (#4829)
- Fixed an issue where a loader's
Request
object reflectedmethod: "POST"
on document submissions (a74e51830
)
- Remove
instanceof Response
checks in favor ofisResponse
(#4782) - Fix performance regression with creation of
@remix-run/router
static handler (#4790) - Update dependency for
@remix-run/router
tov1.0.5
(bd84a9317
)
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.8.1.
- We have been busy at work Layering Remix on top of React Router 6.4 and are excited to be releasing step 1 in this process that consists of performing all server-side data fetches/mutations through the new framework agnostic
@remix-run/router
. Server- and client-side rendering are still done the same as before, and will be updated in subsequent releases. (#4612) - Importing functions and types from the
remix
package is deprecated, and all (#3284) exported modules will be removed in the next major release. For more details, see the release notes for 1.4.0 where these changes were first announced. - Added support for a new route
meta
API to handle arrays of tags instead of an object. For details, check out the RFC. (#4610)
-
Properly categorize internal framework-thrown error Responses as error boundary errors (#4385)
Previously there was some ambiguity around "thrown Responses go to the
CatchBoundary
". TheCatchBoundary
exists to give the user a place to handle non-happy path code flows such that they can throwResponse
instances from their own code and handle them in aCatchBoundary
. However, there are a handful of framework-internal errors that make sense to have a non-500 status code, and the fact that these were being thrown asResponse
instances was causing them to go into theCatchBoundary
, even though they were not user-thrown.With this change, anything thrown by the framework itself (
Error
orResponse
) will go to theErrorBoundary
, and any user-thrownResponse
instances will go to theCatchBoundary
. There is one exception to this rule, which is that framework-detected 404's will continue to go to theCatchBoundary
since users should have one single location to handle 404 displays.The primary affected use cases are scenarios such as:
- HTTP
OPTIONS
requests (405 Unsupported Method ) GET
requests to routes without loaders (400 Bad Request)POST
requests to routes without actions (405 Method Not Allowed)- Missing route id in
_data
parameters (403 Forbidden) - Non-matching route id included in
_data
parameters (403 Forbidden)
- HTTP
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.7.6.
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.7.5.
- Ignore pathless layout routes in action matches (#4376)
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.7.3.
- Fix dependency conflicts with
type-fest
(87642b71b
)
- Properly locked the dependency on
react-router-dom
to version 6.3.0 (#4203)
- We've added a new type:
SerializeFrom
. This is used to infer the (#4013) JSON-serialized return type of loaders and actions. MetaFunction
type can now inferdata
andparentsData
types from route loaders (#4022)
- Improved performance for data serialization at runtime (#3889)
-
We've added type safety for load context.
AppLoadContext
is now an an interface mappingstring
tounknown
, allowing users to extend it via module augmentation: (#1876)declare module "@remix-run/server-runtime" { interface AppLoadContext { // add custom properties here! } }
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.6.7.
No significant changes to this package were made in this release. See the releases page on GitHub for an overview of all changes in v1.6.6.
-
We enhanced the type signatures of
loader
/action
anduseLoaderData
/useActionData
to make it possible to infer the data type from return type of its related server function.To enable this feature, you will need to use the
LoaderArgs
type from your Remix runtime package instead of typing the function directly:- import type { LoaderFunction } from "@remix-run/[runtime]"; + import type { LoaderArgs } from "@remix-run/[runtime]"; - export const loader: LoaderFunction = async (args) => { - return json<LoaderData>(data); - } + export async function loader(args: LoaderArgs) { + return json(data); + }
Then you can infer the loader data by using
typeof loader
as the type variable inuseLoaderData
:- let data = useLoaderData() as LoaderData; + let data = useLoaderData<typeof loader>();
The API above is exactly the same for your route
action
anduseActionData
via theActionArgs
type.With this change you no longer need to manually define a
LoaderData
type (huge time and typo saver!), and we serialize all values so thatuseLoaderData
can't return types that are impossible over the network, such asDate
objects or functions.