{user.name}
RemixFull-stackWeb DevelopmentBest PracticesPerformance
Description
APPLY Remix framework standards WHEN developing with Remix TO ensure consistent and maintainable full-stack applications
Globs
remix.config.js,*.component.tsx
---
description: APPLY Remix framework standards WHEN developing with Remix TO ensure consistent and maintainable full-stack applications
globs: remix.config.js,*.component.tsx
---
# Remix Framework Standards
<version>1.0.0</version>
## Context
- These rules apply to all Remix-specific code in the frontend application
- They ensure consistent implementation of Remix patterns
- They promote maintainable and performant full-stack applications
## Requirements
### Route Structure
- Organize routes by feature/domain
- Use nested routes appropriately
- Implement proper error boundaries
- Use resource routes for API endpoints
### Data Loading
- Use loaders for data fetching ONLY in ROUTES
- Implement proper error handling in loaders
- Use defer for non-critical data
- Implement optimistic UI when appropriate
- Never return `json()`, return plain object
### Form Handling
- Use Remix Form component for all forms
- Implement proper validation
- Use actions for form submissions
- Handle loading and error states
### Performance
- Implement proper caching strategies
- Use prefetching for anticipated routes
- Optimize assets with Remix built-in tools
- Implement proper code splitting
### SEO
- Implement proper meta tags
- Use Links for preloading assets
- Implement proper canonical URLs
- Handle dynamic meta tags
## Examples
<example>
// Good: Well-structured Remix route
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData, Link, useCatch } from "@remix-run/react";
import { getUserById } from "~/models/user.server";
export const meta: MetaFunction = ({ data }) => {
if (!data?.user) {
return [{ title: "User Not Found" }];
}
return [{ title: `${data.user.name}'s Profile` }];
};
export async function loader({ params, request }: LoaderFunctionArgs) {
const userId = params.userId;
if (!userId) {
throw new Response("User ID is required", { status: 400 });
}
const user = await getUserById(userId);
if (!user) {
throw new Response("User not found", { status: 404 });
}
return { user };
}
export default function UserProfile() {
const { user } = useLoaderData<typeof loader>();
return (
<div>
<h1>{user.name}</h1>
{/* Component content */}
<Link to="edit" prefetch="intent">Edit Profile</Link>
</div>
);
}
export function CatchBoundary() {
const caught = useCatch();
return (
<div>
<h1>Error: {caught.status}</h1>
<p>{caught.data}</p>
</div>
);
}
</example>
<example type="invalid">
// Bad: Poorly structured Remix route
import { useEffect, useState } from "react";
export default function UserProfile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
// Client-side data fetching instead of using loader
useEffect(() => {
fetch(`/api/users/${location.pathname.split("/").pop()}`)
.then(res => res.json())
.then(data => {
setUser(data);
setLoading(false);
});
}, []);
// No error handling
// No meta tags
// No error boundary
return (
<div>
{loading ? <p>Loading...</p> : <h1>{user?.name}</h1>}
{/* Regular form instead of Remix Form */}
<form method="post" action="/api/users">
{/* Form content */}
</form>
</div>
);
}
</example>
## Critical Rules
<critical>
- NEVER fetch data client-side when it can be done in a loader
- ALWAYS implement error boundaries
- ALWAYS use Remix Form for forms
- NEVER use direct DOM manipulation
</critical>