All posts

Remix Production Best Practices

Essential Remix production practices including data loading patterns, error boundaries, form handling, caching strategies, and deployment tips.

Remix Production Best Practices

Remix embraces web fundamentals, but production deployments still need careful attention. Here are the practices that matter.

Data Loading Patterns

Keep loaders focused and fast:

// Good: parallel data loading
export async function loader({ params }: LoaderFunctionArgs) {
  const [user, settings] = await Promise.all([
    getUser(params.id),
    getUserSettings(params.id),
  ]);

  if (!user) throw new Response('Not Found', { status: 404 });
  return json({ user, settings });
}

// Better: defer non-critical data
export async function loader({ params }: LoaderFunctionArgs) {
  const user = await getUser(params.id);
  if (!user) throw new Response('Not Found', { status: 404 });

  return defer({
    user,
    recommendations: getRecommendations(user.id), // Streamed
  });
}

Error Boundaries

Every route should have an error boundary:

export function ErrorBoundary() {
  const error = useRouteError();

  if (isRouteErrorResponse(error)) {
    return (
      <div>
        <h1>{error.status} {error.statusText}</h1>
        <p>{error.data}</p>
      </div>
    );
  }

  return (
    <div>
      <h1>Unexpected Error</h1>
      <p>Something went wrong. Please try again.</p>
    </div>
  );
}

Form Handling

Use Remix's progressive enhancement:

export function OrderForm() {
  const navigation = useNavigation();
  const isSubmitting = navigation.state === 'submitting';

  return (
    <Form method="post">
      <input name="product" required />
      <button disabled={isSubmitting}>
        {isSubmitting ? 'Processing...' : 'Place Order'}
      </button>
    </Form>
  );
}

Caching Strategy

export async function loader() {
  const data = await getPublicData();
  return json(data, {
    headers: {
      'Cache-Control': 'public, max-age=300, s-maxage=3600',
    },
  });
}

Security

  • Validate all form data server-side in actions
  • Use CSRF tokens for state-changing operations
  • Sanitize user input before rendering
  • Set security headers in entry.server.tsx

Monitoring

Remix errors in loaders and actions can be silent. Integrate Bugsly to capture thrown responses and unhandled exceptions, giving visibility into server-side failures that error boundaries mask from users.

Try Bugsly Free

AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.

Get Started Free