JSPM

  • Created
  • Published
  • Downloads 211
  • Score
    100M100P100Q87260F
  • License MIT

Package Exports

  • server-act
  • server-act/package.json

Readme

Server-Act

npm version

A simple React server action builder that provides input validation with zod.

Installation

# npm
npm install server-act zod

# yarn
yarn add server-act zod

# pnpm
pnpm add server-act zod

Usage

// action.ts
"use server";

import { serverAct } from "server-act";

export const sayHelloAction = serverAct
  .input(
    z.object({
      name: z.string(),
    })
  )
  .action(async ({ input }) => {
    return `Hello, ${input.name}`;
  });
// client-component.tsx
"use client";

import { sayHelloAction } from "./action";

export const ClientComponent = () => {
  const onClick = () => {
    const message = await sayHelloAction({ name: "John" });
    console.log(message); // Hello, John
  };

  return (
    <div>
      <button onClick={onClick}>Trigger action</button>
    </div>
  );
};

With Middleware

// action.ts
"use server";

import { serverAct } from "server-act";

export const sayHelloAction = serverAct
  .middleware(() => {
    const userId = "...";
    return { userId };
  })
  .input(
    z.object({
      name: z.string(),
    })
  )
  .action(async ({ ctx, input }) => {
    console.log("User ID", ctx.userId);
    return `Hello, ${input.name}`;
  });

(Experimental) useFormState Support

useFormState Documentation: https://nextjs.org/docs/app/building-your-application/data-fetching/forms-and-mutations#error-handling

// action.ts;
"use server";

import { serverAct } from "server-act";

export const sayHelloAction = serverAct
  .middleware(requestTimeMiddleware)
  .input(
    z.object({
      name: z
        .string({ required_error: `You haven't told me your name` })
        .nonempty({ message: "You need to tell me your name!" }),
    })
  )
  .experimental_formAction(async ({ input, formErrors, ctx }) => {
    if (formErrors) {
      return { formErrors: formErrors.formErrors.fieldErrors };
    }
    return { message: `Hello, ${input.name}!` };
  });
// client-component.tsx
"use client";

import { sayHelloAction } from "./action";

export const ClientComponent = () => {
  const [state, dispatch] = useFormState(sayHelloAction, { formErrors: {} });

  return (
    <form action={dispatch}>
      <input name="name" required />
      {state.formErrors?.name?.map((error) => <p key={error}>{error}</p>)}

      <button type="submit">Submit</button>

      {!!state.message && <p>{state.message}</p>}
    </form>
  );
};