Generate fullstack data flow patterns for Remix applications using loaders, actions, and forms with automatic UI sync
This skill has safety concerns that you should review before use. Some patterns were detected that may pose a risk.Safety score: 75/100.
KillerSkills scans all public content for safety. Use caution before installing or executing flagged content.
Generate fullstack data flow patterns for Remix applications following official conventions for loaders, actions, and forms with automatic UI synchronization.
This skill helps you implement Remix's three-step data flow pattern:
1. Route loaders provide data to the UI
2. Forms post data to route actions that update persistent state
3. Loader data on the page is automatically revalidated
It generates route modules with properly typed loaders, actions, and components that keep UI in sync with server state.
When the user requests help with Remix data flow, forms, loaders, or actions:
1. **Analyze the Request**
- Identify the data entity (user, post, product, etc.)
- Determine CRUD operations needed (read, create, update, delete)
- Note any specific validation or error handling requirements
- Check if progressive enhancement (pre-JavaScript) is needed
2. **Generate Route Module Structure**
Create a complete route file with three exports:
- `loader` function for data fetching
- default `Component` export for UI rendering
- `action` function for data mutations
3. **Implement Loader Function**
```typescript
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
export async function loader({ request, params }: LoaderFunctionArgs) {
// Extract authentication/authorization
const user = await getUser(request);
// Fetch data from database/API
const data = await fetchData(params.id);
// Return JSON response
return json({ data });
}
```
4. **Implement Component with useLoaderData**
```typescript
import { useLoaderData, Form } from "@remix-run/react";
export default function Component() {
const { data } = useLoaderData<typeof loader>();
return (
<Form method="post" action="/route-path">
{/* Form fields with name attributes matching formData keys */}
<input name="fieldName" defaultValue={data.fieldName} />
<button type="submit">Save</button>
</Form>
);
}
```
5. **Implement Action Function**
```typescript
import type { ActionFunctionArgs } from "@remix-run/node";
export async function action({ request, params }: ActionFunctionArgs) {
// Parse form data
const formData = await request.formData();
// Extract values using name attributes
const fieldValue = formData.get("fieldName");
// Perform mutation
await updateData(params.id, { fieldValue });
// Return response (loaders auto-revalidate)
return json({ ok: true });
}
```
6. **Add TypeScript Types**
- Use `LoaderFunctionArgs` and `ActionFunctionArgs` types
- Type `useLoaderData` with `typeof loader`
- Export proper return types for type safety
7. **Handle Form Submissions**
- Ensure input `name` attributes match `formData.get()` keys
- Use `method="post"` for mutations
- Set `action` attribute to route path (can be same route)
8. **Consider Progressive Enhancement**
- Ensure forms work before JavaScript loads
- Browser submits form to action if JS not loaded
- Remix renders updated HTML after action completes
9. **Add Pending States (Optional)**
If user needs loading indicators:
```typescript
import { useNavigation } from "@remix-run/react";
const navigation = useNavigation();
const isSubmitting = navigation.state === "submitting";
```
10. **Implement Error Handling (Optional)**
If user needs error boundaries:
```typescript
export function ErrorBoundary() {
const error = useRouteError();
return <div>Error: {error.message}</div>;
}
```
```typescript
// app/routes/users.$id.tsx
import type { LoaderFunctionArgs, ActionFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData, Form } from "@remix-run/react";
export async function loader({ params }: LoaderFunctionArgs) {
const user = await db.user.findUnique({ where: { id: params.id } });
return json({ user });
}
export default function UserEdit() {
const { user } = useLoaderData<typeof loader>();
return (
<Form method="post">
<input name="name" defaultValue={user.name} />
<input name="email" defaultValue={user.email} />
<button type="submit">Save</button>
</Form>
);
}
export async function action({ request, params }: ActionFunctionArgs) {
const formData = await request.formData();
await db.user.update({
where: { id: params.id },
data: {
name: formData.get("name"),
email: formData.get("email"),
},
});
return json({ ok: true });
}
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/remix-data-flow/raw