Skip to content

Server Action

React Server Actions let client components invoke asynchronous server functions. With oRPC, you simply append the .actionable modifier to enable Server Action compatibility.

Server Side

Define your procedure with .actionable for Server Action support.

ts
'use server'

import { 
redirect
} from 'next/navigation'
export const
ping
=
os
.
input
(
z
.
object
({
name
:
z
.
string
() }))
.
handler
(async ({
input
}) => `Hello, ${
input
.
name
}`)
.
actionable
({
context
: async () => ({}), // Optional: provide initial context if needed
interceptors
: [
onSuccess
(async
output
=>
redirect
(`/some-where`)),
onError
(async
error
=>
console
.
error
(
error
)),
], })

TIP

We recommend using Runtime Context instead of Initial Context when working with Server Actions.

Client Side

On the client, import and call your procedure as follows:

tsx
'use client'

import { ping } from './actions'

export function MyComponent() {
  const [name, setName] = useState('')

  const handleSubmit = async (e: FormEvent) => {
    e.preventDefault()
    const [error, data] = await ping({ name })
    console.log(error, data)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={name} onChange={e => setName(e.target.value)} />
      <button type="submit">Submit</button>
    </form>
  )
}

This approach seamlessly integrates server-side procedures with client components via Server Actions.

Type‑Safe Error Handling

The .actionable modifier supports type-safe error handling with a JSON-like error object.

ts
'use client'

const [
error
,
data
] = await
someAction
({
name
: 'John' })
if (
error
) {
if (
error
.
defined
) {
console
.
log
(
error
.
data
)
// ^ Typed error data } // Handle unknown errors } else { // Handle success
console
.
log
(
data
)
}

@orpc/react Package

The @orpc/react package offers utilities to integrate oRPC with React and React Server Actions.

Installation

sh
npm install @orpc/react@latest
sh
yarn add @orpc/react@latest
sh
pnpm add @orpc/react@latest
sh
bun add @orpc/react@latest
sh
deno install npm:@orpc/react@latest

useServerAction Hook

The useServerAction hook simplifies invoking server actions in React.

tsx
'use client'

import { 
useServerAction
} from '@orpc/react/hooks'
import {
isDefinedError
,
onError
} from '@orpc/client'
export function
MyComponent
() {
const {
execute
,
data
,
error
,
status
} =
useServerAction
(
someAction
, {
interceptors
: [
onError
((
error
) => {
if (
isDefinedError
(
error
)) {
console
.
error
(
error
.
data
)
// ^ Typed error data } }), ], }) const
action
= async (
form
: FormData) => {
const
name
=
form
.
get
('name') as string
execute
({
name
})
} return ( <
form
action
={
action
}>
<
input
type
="text"
name
="name"
required
/>
<
button
type
="submit">Submit</
button
>
{
status
=== 'pending' && <
p
>Loading...</
p
>}
</
form
>
) }

createFormAction Utility

The createFormAction utility accepts a procedure and returns a function to handle form submissions. It uses Bracket Notation to deserialize form data.

tsx
import { createFormAction } from '@orpc/react'

const dosomething = os
  .input(
    z.object({
      user: z.object({
        name: z.string(),
        age: z.coerce.number(),
      }),
    })
  )
  .handler(({ input }) => {
    console.log('Form action called!')
    console.log(input)
  })

export const redirectSomeWhereForm = createFormAction(dosomething, {
  interceptors: [
    onSuccess(async () => {
      redirect('/some-where')
    }),
  ],
})

export function MyComponent() {
  return (
    <form action={redirectSomeWhereForm}>
      <input type="text" name="user[name]" required />
      <input type="number" name="user[age]" required />
      <button type="submit">Submit</button>
    </form>
  )
}

By moving the redirect('/some-where') logic into createFormAction rather than the procedure, you enhance the procedure's reusability beyond Server Actions.

INFO

When using createFormAction, any ORPCError with a status of 401, 403, or 404 is automatically converted into the corresponding Next.js error responses: unauthorized, forbidden, and not found.

Released under the MIT License.