React DOM bindings for Remix web framework providing components, hooks, and utilities for full-stack React applications
—
Server runtime utilities for creating responses, redirects, and handling deferred data in Remix loader and action functions.
Create JSON responses with proper headers and serialization.
/**
* Creates a Response with JSON data and appropriate headers
* Automatically serializes data and sets Content-Type header
* @param data - Data to serialize as JSON
* @param init - Optional response initialization options
* @returns Response object with JSON data
*/
function json<Data>(data: Data, init?: ResponseInit): Response;Usage Examples:
import { json } from "@remix-run/react";
import type { LoaderFunctionArgs } from "@remix-run/node";
// Basic JSON response
export async function loader({ params }: LoaderFunctionArgs) {
const user = await getUser(params.userId);
if (!user) {
throw json({ error: "User not found" }, { status: 404 });
}
return json({
user,
lastUpdated: new Date(),
preferences: user.preferences,
});
}
// JSON response with custom headers
export async function loader() {
const data = await getPublicData();
return json(data, {
headers: {
"Cache-Control": "public, max-age=3600",
"X-Custom-Header": "value",
},
});
}
// JSON error response
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
try {
const result = await processForm(formData);
return json({ success: true, result });
} catch (error) {
return json(
{
success: false,
error: error.message,
field: "email"
},
{ status: 400 }
);
}
}Create redirect responses for navigation and flow control.
/**
* Creates a redirect Response to the specified URL
* @param url - URL to redirect to
* @param init - Optional response initialization options
* @returns Response object with redirect status and Location header
*/
function redirect(url: string, init?: ResponseInit): Response;
/**
* Creates a redirect Response that reloads the entire document
* Forces a full page reload instead of client-side navigation
* @param url - URL to redirect to
* @param init - Optional response initialization options
* @returns Response object with redirect and document reload
*/
function redirectDocument(url: string, init?: ResponseInit): Response;
/**
* Creates a redirect Response that replaces the current entry in the history stack
* Uses replace navigation instead of pushing a new entry
* @param url - URL to redirect to
* @param init - Optional response initialization options (defaults to 302)
* @returns Response object with redirect status and Location header
*/
function replace(url: string, init?: ResponseInit): Response;Usage Examples:
import { redirect, redirectDocument, replace } from "@remix-run/react";
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
// Basic redirect
export async function loader({ request }: LoaderFunctionArgs) {
const user = await getCurrentUser(request);
if (!user) {
return redirect("/login");
}
return json({ user });
}
// Redirect with custom status
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const result = await createPost(formData);
// Redirect to the new post with 201 status
return redirect(`/posts/${result.id}`, { status: 201 });
}
// Document redirect (full page reload)
export async function action({ request }: ActionFunctionArgs) {
await processPayment(request);
// Force full page reload for payment confirmation
return redirectDocument("/payment/success");
}
// Replace redirect (replaces current history entry)
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
const result = await updateProfile(formData);
// Replace current URL instead of adding to history
return replace("/profile", { status: 302 });
}
// Conditional redirect
export async function loader({ request, params }: LoaderFunctionArgs) {
const user = await getCurrentUser(request);
const post = await getPost(params.postId);
if (!post) {
throw json({ error: "Post not found" }, { status: 404 });
}
if (post.private && post.authorId !== user?.id) {
return redirect("/unauthorized");
}
return json({ post, user });
}Create responses with deferred data for streaming and progressive loading.
/**
* Creates a Response with deferred data for streaming
* Allows some data to load immediately while other data streams in later
* @param data - Object containing immediate and deferred data
* @param init - Optional response initialization options
* @returns Response object with streaming deferred data
*/
function defer<Data extends Record<string, unknown>>(
data: Data,
init?: ResponseInit
): Response;Usage Examples:
import { defer, json } from "@remix-run/react";
import { Await, useLoaderData } from "@remix-run/react";
import { Suspense } from "react";
import type { LoaderFunctionArgs } from "@remix-run/node";
// Deferred data loading
export async function loader({ params }: LoaderFunctionArgs) {
const user = await getUser(params.userId); // Fast query
// Defer slow queries
const posts = getUserPosts(params.userId); // Don't await
const analytics = getUserAnalytics(params.userId); // Don't await
return defer({
user, // Immediate data
posts, // Deferred promise
analytics, // Deferred promise
});
}
// Component using deferred data
export default function UserProfile() {
const { user, posts, analytics } = useLoaderData<typeof loader>();
return (
<div>
<h1>{user.name}</h1>
<Suspense fallback={<div>Loading posts...</div>}>
<Await resolve={posts}>
{(resolvedPosts) => (
<PostsList posts={resolvedPosts} />
)}
</Await>
</Suspense>
<Suspense fallback={<div>Loading analytics...</div>}>
<Await resolve={analytics}>
{(resolvedAnalytics) => (
<AnalyticsDashboard data={resolvedAnalytics} />
)}
</Await>
</Suspense>
</div>
);
}
// Mixed immediate and deferred data
export async function loader() {
const criticalData = await getCriticalData(); // Must load first
const slowData = getSlowData(); // Defer this
return defer({
critical: criticalData,
slow: slowData,
metadata: { loadTime: Date.now() },
});
}Create responses that replace the current history entry.
/**
* Creates a response that replaces the current history entry
* Useful for preventing back button navigation to intermediate states
* @param url - URL to replace with
* @param init - Optional response initialization options
* @returns Response object with replace behavior
*/
function replace(url: string, init?: ResponseInit): Response;Usage Examples:
import { replace, json } from "@remix-run/react";
import type { ActionFunctionArgs } from "@remix-run/node";
// Replace after form submission
export async function action({ request }: ActionFunctionArgs) {
const formData = await request.formData();
if (formData.get("step") === "confirm") {
await processOrder(formData);
// Replace the confirmation step in history
return replace("/order/success");
}
return json({ step: "confirm" });
}Create flexible data responses for various content types.
/**
* Creates a generic data response with flexible content handling
* Supports various data types and custom serialization
* @param data - Data to include in the response
* @param init - Optional response initialization options
* @returns Response object with the provided data
*/
function data<Data>(data: Data, init?: ResponseInit): Response;Usage Examples:
import { data } from "@remix-run/react";
// Custom response with specific content type
export async function loader() {
const csvData = await generateCSVReport();
return data(csvData, {
headers: {
"Content-Type": "text/csv",
"Content-Disposition": "attachment; filename=report.csv",
},
});
}
// Binary data response
export async function loader({ params }: LoaderFunctionArgs) {
const imageBuffer = await getImage(params.imageId);
return data(imageBuffer, {
headers: {
"Content-Type": "image/jpeg",
"Cache-Control": "public, max-age=86400",
},
});
}interface ResponseInit {
/** HTTP status code */
status?: number;
/** HTTP status text */
statusText?: string;
/** Response headers */
headers?: HeadersInit;
}
type HeadersInit = Headers | string[][] | Record<string, string>;/**
* Type for deferred data objects
* Keys can contain immediate values or promises that resolve later
*/
type DeferredData<T extends Record<string, unknown>> = {
[K in keyof T]: T[K] | Promise<T[K]>;
};Install with Tessl CLI
npx tessl i tessl/npm-remix-run--react