A type-safe wrapper for React Server Actions with Yup schema validation.
- 🛡️ Type-safe: Full TypeScript support with inferred types
- ✅ Validation: Seamless integration with Yup schemas
- 🧩 Flexible: Support for actions with or without input
- 🪝 React Hook: Easy integration with React components
- 📊 Status Tracking: Built-in state management for pending, success, and error states
npm install yup-server-action
# or
yarn add yup-server-action
# or
pnpm add yup-server-action
// _action.ts
import { createServerAction } from "yup-server-action";
import * as y from "yup";
const UserSchema = y.object({
name: y.string().required(),
email: y.string().email().required(),
password: y.string().required(),
});
export const saveUserAction = createServerAction()
.input(UserSchema)
.handle(async ({ input }) => {
// Perform your server-side logic here
// e.g., save to database, call external API, etc.
return {
user: input,
};
});
// page.tsx
import React from "react";
import { useServerAction } from "yup-server-action";
import { saveUserAction } from "./_action";
export default function CreateUserPage() {
const { execute, isPending, isSuccess, isError, error, data } =
useServerAction(saveUserAction, {
onSuccess(result) {
alert("User created");
console.log(result);
},
onError(err) {
alert("Error creating user");
console.error(err);
},
});
if (isPending) {
return <button disabled>Creating user...</button>;
}
if (isError) {
return (
<div>
<h1>Error creating user</h1>
<pre>{JSON.stringify(error, null, 2)}</pre>
</div>
);
}
if (isSuccess) {
return (
<div>
<h1>User created</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
return (
<div>
<h1>Create user</h1>
<form
onSubmit={(e) => {
e.preventDefault();
execute({
name: e.currentTarget.userName.value,
email: e.currentTarget.email.value,
password: e.currentTarget.password.value,
});
}}
>
<label htmlFor="userName">Name</label>
<input id="userName" name="userName" type="text" />
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" />
<label htmlFor="password">Password</label>
<input id="password" name="password" type="password" />
<button type="submit">Create user</button>
</form>
</div>
);
}
// _action.ts
import { createServerAction } from "yup-server-action";
export const getMyUserAction = createServerAction().handle(async () => {
// Fetch user data or perform any server-side operation
return {
data: {
// User data here
},
};
});
// page.tsx
import React from "react";
import { useServerAction } from "yup-server-action";
import { getMyUserAction } from "./_action";
export default function UserProfilePage() {
const { execute, isPending, isSuccess, isError, error, data } =
useServerAction(getMyUserAction, {
onSuccess(result) {
console.log(result);
},
});
if (isPending) {
return <div>Loading user data...</div>;
}
if (isError) {
return <div>Error loading user data</div>;
}
return (
<div>
<h1>User Profile</h1>
<button onClick={() => execute()}>Load User Data</button>
{isSuccess && (
<div>
<h2>User Data</h2>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)}
</div>
);
}
Creates a new server action builder.
// Without input schema
createServerAction().handle(handler)
// With input schema
createServerAction(schema).handle(handler)
// or
createServerAction().input(schema).handle(handler)
React hook for using server actions in components.
const {
execute, // Function to execute the server action
isPending, // Boolean indicating if the action is in progress
isSuccess, // Boolean indicating if the action completed successfully
isError, // Boolean indicating if the action resulted in an error
data, // The data returned by the action (if successful)
error // The error thrown by the action (if failed)
} = useServerAction(action, {
onSuccess, // Optional callback for successful execution
onError // Optional callback for failed execution
});
- Simplified Validation: Leverage Yup's powerful schema validation without boilerplate
- Type Safety: Get full TypeScript support with inferred types from your schemas
- React Integration: Easily use server actions in your React components with built-in state management
- Developer Experience: Improve your DX with a clean, fluent API
Enzo Apolinário