better-react-server-actions
TypeScript icon, indicating that this package has built-in type declarations

0.0.0-alpha7 • Public • Published

Getting Started

So you're like me and you love Server Actions, but you need more Zod and TypeScript!

Why Use This Library?

Why React 19+ is required

This library is designed to enhance useActionState, which requires React 19. Without that, you really won't get much benefit from this library.

I have only tested with Next.js for now, but the goal is to make this work with React in general.

Documentation

See christianjuth.github.io/better-react-server-actions.

Example

Login Form Example

"use server";
 
import { createActionWithState } from 'better-react-server-actions';
import { zfd } from 'zod-form-data';
import { z } from 'zod';
import { redirect } from 'next/navigation';
 
const EMAIL = 'admin@example.com';
const PASSWORD = 'password';
 
export const login = createActionWithState({
  formDataSchema: zfd.formData({
    email: z.string().email(),
    password: zfd.text(),
  }),
  requestHandler: async (prevState, { email, password }) => {
    if (email !== EMAIL || password !== PASSWORD) {
      throw new Error('Invalid email or password');
    }
 
    redirect('/examples/success')
  }
});
"use client";
 
import { useActionState } from 'react';
import { login } from './action';
 
export default function Page() {
  const [state, action] = useActionState(login, {});
 
  const formErrors = state.errors?.formErrors;
 
  return (
    <form action={action}>
      <h1>Login</h1>
 
      {state.errors?.actionErrors && (
        <span>
          {state.errors.actionErrors.join(', ')}
        </span>
      )}
 
      <label htmlFor="email">Email:</label>
      <input 
        id="email" 
        name="email" 
      />
      {formErrors?.email && (
        <span>
          {formErrors.email.join(', ')}
        </span>
      )}
 
      <label htmlFor="password">Password:</label>
      <input 
        id="password" 
        name="password" 
        type="password" 
      />
      {formErrors?.password && (
        <span>
          {formErrors.password.join(', ')}
        </span>
      )}
 
      <button>
        Login
      </button>
    </form>
  )
}

Like Button Example

"use server";
 
import { createActionWithState } from 'better-react-server-actions';
import { z } from 'zod';
 
export const login = createActionWithState({
  stateSchema: z.object({
    likeId: z.string().optional(),
  }),
  requestHandler: async ({ likeId }) => {
    // Check if user is logged in
    // and redirect to login page if not
 
    if (likeId) {
      // Delete like via api
      return {
        likeId: undefined,
      }
    } else {
      // Create like via api
      const newLikeId = 'new-like-id';
      return {
        likeId: newLikeId,
      }
    }
  }
});
"use client";
 
import { useActionState } from 'react';
import { toggleLike } from './action';
 
export default function Page() {
  const [state, action] = useActionState(toggleLike, {});
 
  return (
    <form action={action}>
      <button>
        {state.likeId ? 'Unlike' : 'Like'}
      </button>
    </form>
  )
}

Increment Counter Example

"use server";
 
import { createActionWithState } from 'better-react-server-actions';
import { z } from 'zod';
 
export const incrementCounter = createActionWithState({
  stateSchema: z.object({
    count: z.number().min(0),
  }),
  requestHandler: async ({ count }) => {
    return {
      count: count + 1,
    }
  }
});
"use client";
 
import { useActionState } from 'react';
import { incrementCounter } from './action';
 
export default function Page() {
  const [state, action] = useActionState(incrementCounter, {
    count: 0,
  });
 
  return (
    <form action={action}>
      <span>Count: {state.count}</span>
      <button>
        Increment
      </button>
    </form>
  )
}

Package Sidebar

Install

npm i better-react-server-actions

Weekly Downloads

418

Version

0.0.0-alpha7

License

MIT

Unpacked Size

39.7 kB

Total Files

21

Last publish

Collaborators

  • cjuth