Actions
One question you will likely have when developing any sort of app is "how do I communicate new information to my server?". The user did something. What next? Solid Router's answer to this is actions.
Actions give you the ability to specify an async action processing function and gives you elegant tools to help you easily manage and track submissions. Actions are isomorphic and represent a POST
request.
Actions are isomorphic. This means that a submission can be handled on the server or the client, whichever is optimal. They represent the server component of an HTML form, and even help you use HTML forms to submit data.
Creating actions
Let's stop getting ahead of ourselves! First, let's create an action!
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, useAction(alias) function useAction<T extends any[], U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>
import useAction
} from "@solidjs/router";
const echoconst echo: Action<[message: string], void>
= action(alias) action<[message: string], void>(fn: (message: string) => Promise<void>, name?: string | undefined): Action<[message: string], void>
import action
(async (message(parameter) message: string
: string) => { // Imagine this is a call to fetch
await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); console.log[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
(method) Console.log(...data: any[]): void
(message(parameter) message: string
); });
export function MyComponentfunction MyComponent(): void
() { const myEchoconst myEcho: (message: string) => Promise<void>
= useAction(alias) useAction<[message: string], void>(action: Action<[message: string], void>): (message: string) => Promise<void>
import useAction
(echoconst echo: Action<[message: string], void>
); }
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, useAction(alias) function useAction<T extends any[], U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>
import useAction
} from "@solidjs/router";
const echoconst echo: Action<[message: string], void>
= action(alias) action<[message: string], void>(fn: (message: string) => Promise<void>, name?: string | undefined): Action<[message: string], void>
import action
(async (message(parameter) message: string
: string) => { // Imagine this is a call to fetch
await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); console.log[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
(method) Console.log(...data: any[]): void
(message(parameter) message: string
); });
export function MyComponentfunction MyComponent(): void
() { const myEchoconst myEcho: (message: string) => Promise<void>
= useAction(alias) useAction<[message: string], void>(action: Action<[message: string], void>): (message: string) => Promise<void>
import useAction
(echoconst echo: Action<[message: string], void>
); }
This echo
action will act as your backend, however you can substitute it for any API, provided you are ok with it running on the client. Typically, route actions are used with some sort of solution like fetch or GraphQL.
These will return either a Response
such as a redirect (we are not returning anything quite yet!) or any value. If you want to ensure the action only runs on the server for things like databases, you will want to use "use server"
, introduced below.
Naturally, this action won't do anything quite yet. We still need to call it somewhere! For now, let's call it manually from some component using the submit function returned from action
.
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, useAction(alias) function useAction<T extends any[], U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>
import useAction
} from "@solidjs/router";
const echoconst echo: Action<[message: string], void>
= action(alias) action<[message: string], void>(fn: (message: string) => Promise<void>, name?: string | undefined): Action<[message: string], void>
import action
(async (message(parameter) message: string
: string) => { // Imagine this is a call to fetch
await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); console.log[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
(method) Console.log(...data: any[]): void
(message(parameter) message: string
); });
export function MyComponentfunction MyComponent(): void
() { const myEchoconst myEcho: (message: string) => Promise<void>
= useAction(alias) useAction<[message: string], void>(action: Action<[message: string], void>): (message: string) => Promise<void>
import useAction
(echoconst echo: Action<[message: string], void>
); myEchoconst myEcho: (message: string) => Promise<void>
("Hello from solid!"); }
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, useAction(alias) function useAction<T extends any[], U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>
import useAction
} from "@solidjs/router";
const echoconst echo: Action<[message: string], void>
= action(alias) action<[message: string], void>(fn: (message: string) => Promise<void>, name?: string | undefined): Action<[message: string], void>
import action
(async (message(parameter) message: string
: string) => { // Imagine this is a call to fetch
await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); console.log[MDN Reference](https://developer.mozilla.org/docs/Web/API/console/log)
(method) Console.log(...data: any[]): void
(message(parameter) message: string
); });
export function MyComponentfunction MyComponent(): void
() { const myEchoconst myEcho: (message: string) => Promise<void>
= useAction(alias) useAction<[message: string], void>(action: Action<[message: string], void>): (message: string) => Promise<void>
import useAction
(echoconst echo: Action<[message: string], void>
); myEchoconst myEcho: (message: string) => Promise<void>
("Hello from solid!"); }
You should see Hello from solid!
back in the console!
Returning from actions
In many cases, after submitting data the server sends some data back as well. Usually an error message if something failed. Anything returned from your action function can be accessed using the reactive action.result
property. The value of this property can change each time you submit your action.
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, useAction(alias) function useAction<T extends any[], U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>
import useAction
, useSubmission(alias) function useSubmission<T extends any[], U>(fn: Action<T, U>, filter?: ((arg: T) => boolean) | undefined): Submission<T, U>
import useSubmission
} from "@solidjs/router";
const echoconst echo: Action<[message: string], string>
= action(alias) action<[message: string], string>(fn: (message: string) => Promise<string>, name?: string | undefined): Action<[message: string], string>
import action
(async (message(parameter) message: string
: string) => { await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); return message(parameter) message: string
; });
export function MyComponentfunction MyComponent(): JSX.Element
() { const myEchoconst myEcho: (message: string) => Promise<string>
= useAction(alias) useAction<[message: string], string>(action: Action<[message: string], string>): (message: string) => Promise<string>
import useAction
(echoconst echo: Action<[message: string], string>
); const echoingconst echoing: Submission<[message: string], string>
= useSubmission(alias) useSubmission<[message: string], string>(fn: Action<[message: string], string>, filter?: ((arg: [message: string]) => boolean) | undefined): Submission<[message: string], string>
import useSubmission
(echoconst echo: Action<[message: string], string>
); myEchoconst myEcho: (message: string) => Promise<string>
("Hello from solid!"); setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(() => myEchoconst myEcho: (message: string) => Promise<string>
("This is a second submission!"), 1500); return <p(property) JSX.HTMLElementTags.p: JSX.HTMLAttributes<HTMLParagraphElement>
>{echoingconst echoing: Submission<[message: string], string>
.result(property) result?: string | undefined
}</p(property) JSX.HTMLElementTags.p: JSX.HTMLAttributes<HTMLParagraphElement>
>; }
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, useAction(alias) function useAction<T extends any[], U>(action: Action<T, U>): (...args: Parameters<Action<T, U>>) => Promise<U>
import useAction
, useSubmission(alias) function useSubmission<T extends any[], U>(fn: Action<T, U>, filter?: ((arg: T) => boolean) | undefined): Submission<T, U>
import useSubmission
} from "@solidjs/router";
const echoconst echo: Action<[message: string], string>
= action(alias) action<[message: string], string>(fn: (message: string) => Promise<string>, name?: string | undefined): Action<[message: string], string>
import action
(async (message(parameter) message: string
: string) => { await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); return message(parameter) message: string
; });
export function MyComponentfunction MyComponent(): JSX.Element
() { const myEchoconst myEcho: (message: string) => Promise<string>
= useAction(alias) useAction<[message: string], string>(action: Action<[message: string], string>): (message: string) => Promise<string>
import useAction
(echoconst echo: Action<[message: string], string>
); const echoingconst echoing: Submission<[message: string], string>
= useSubmission(alias) useSubmission<[message: string], string>(fn: Action<[message: string], string>, filter?: ((arg: [message: string]) => boolean) | undefined): Submission<[message: string], string>
import useSubmission
(echoconst echo: Action<[message: string], string>
); myEchoconst myEcho: (message: string) => Promise<string>
("Hello from solid!"); setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(() => myEchoconst myEcho: (message: string) => Promise<string>
("This is a second submission!"), 1500); return <p(property) JSX.HTMLElementTags.p: JSX.HTMLAttributes<HTMLParagraphElement>
>{echoingconst echoing: Submission<[message: string], string>
.result(property) result?: string | undefined
}</p(property) JSX.HTMLElementTags.p: JSX.HTMLAttributes<HTMLParagraphElement>
>; }
While this method of using actions works, it leaves the implementation details of how you trigger echo
up to you. When handling explicit user input, it's better to use a form
for a multitude of reasons.
We highly recommend using HTML forms as your method to submit data with actions. HTML forms can be used even before JavaScript loads, leading to instantly interactive applications.
They have the added benefit of implicit accessibility. They can save you valuable time that would have otherwise been spent designing a UI library that will never have the aforementioned benefits.
When forms are used to submit actions, the first argument is an instance of FormData
. To write a form using actions, pass the action to the action property of your form. You can then walk away with amazing, progressively enhanced forms!
If you don't return a Response
from your action, the user will stay on the same page and your resources will be re-triggered. You can also throw a redirect
to tell the browser to navigate.
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, redirect(alias) function redirect(url: string, init?: number | RouterResponseInit): never
import redirect
} from "@solidjs/router";
const isAdminconst isAdmin: Action<[formData: FormData], Error>
= action(alias) action<[formData: FormData], Error>(fn: (formData: FormData) => Promise<Error>, name?: string | undefined): Action<[formData: FormData], Error>
import action
(async (formData(parameter) formData: FormData
: FormDataProvides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData)
interface FormData
) => { await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); const usernameconst username: FormDataEntryValue | null
= formData(parameter) formData: FormData
.get[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get)
(method) FormData.get(name: string): FormDataEntryValue | null
("username"); if (usernameconst username: FormDataEntryValue | null
=== "admin") throw redirect(alias) redirect(url: string, init?: number | RouterResponseInit | undefined): never
import redirect
("/admin"); return new Errorvar Error: ErrorConstructor
new (message?: string | undefined) => Error
("Invalid username"); });
export function MyComponentfunction MyComponent(): JSX.Element
() { return (
<form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
action(property) JSX.FormHTMLAttributes<HTMLFormElement>.action?: string | JSX.SerializableAttributeValue | undefined
={isAdminconst isAdmin: Action<[formData: FormData], Error>
} method(property) JSX.FormHTMLAttributes<HTMLFormElement>.method?: JSX.HTMLFormMethod | undefined
="post"> <label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
for(property) JSX.LabelHTMLAttributes<HTMLLabelElement>.for?: string | undefined
="username">Username:</label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="text" name(property) JSX.InputHTMLAttributes<HTMLInputElement>.name?: string | undefined
="username" /> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="submit" value(property) JSX.InputHTMLAttributes<HTMLInputElement>.value?: string | number | string[] | undefined
="submit" /> </form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
> );
}
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, redirect(alias) function redirect(url: string, init?: number | RouterResponseInit): never
import redirect
} from "@solidjs/router";
const isAdminconst isAdmin: Action<[formData: FormData], Error>
= action(alias) action<[formData: FormData], Error>(fn: (formData: FormData) => Promise<Error>, name?: string | undefined): Action<[formData: FormData], Error>
import action
(async (formData(parameter) formData: FormData
: FormDataProvides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData)
interface FormData
) => { await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); const usernameconst username: FormDataEntryValue | null
= formData(parameter) formData: FormData
.get[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get)
(method) FormData.get(name: string): FormDataEntryValue | null
("username"); if (usernameconst username: FormDataEntryValue | null
=== "admin") throw redirect(alias) redirect(url: string, init?: number | RouterResponseInit | undefined): never
import redirect
("/admin"); return new Errorvar Error: ErrorConstructor
new (message?: string | undefined) => Error
("Invalid username"); });
export function MyComponentfunction MyComponent(): JSX.Element
() { return (
<form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
action(property) JSX.FormHTMLAttributes<HTMLFormElement>.action?: string | JSX.SerializableAttributeValue | undefined
={isAdminconst isAdmin: Action<[formData: FormData], Error>
} method(property) JSX.FormHTMLAttributes<HTMLFormElement>.method?: JSX.HTMLFormMethod | undefined
="post"> <label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
for(property) JSX.LabelHTMLAttributes<HTMLLabelElement>.for?: string | undefined
="username">Username:</label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="text" name(property) JSX.InputHTMLAttributes<HTMLInputElement>.name?: string | undefined
="username" /> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="submit" value(property) JSX.InputHTMLAttributes<HTMLInputElement>.value?: string | number | string[] | undefined
="submit" /> </form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
> );
}
Server Actions
Sometimes we need to make sure our action only runs on the server. This is useful for:
- Accessing internal APIs.
- Proxying external APIs.
- To use server secrets.
- To reduce the response payload by postprocessing.
- To bypass CORS.
- Running code incompatible with browsers.
- Or even connecting directly to a database. (Take caution, opinions on if this is a good idea are mixed. You should consider separating your backend and frontend).
To do this, put a "use server";
directive in your action function:
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, redirect(alias) function redirect(url: string, init?: number | RouterResponseInit): never
import redirect
} from "@solidjs/router";
const isAdminconst isAdmin: Action<[formData: FormData], Error>
= action(alias) action<[formData: FormData], Error>(fn: (formData: FormData) => Promise<Error>, name?: string | undefined): Action<[formData: FormData], Error>
import action
(async (formData(parameter) formData: FormData
: FormDataProvides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData)
interface FormData
) => { "use server";
await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); const usernameconst username: FormDataEntryValue | null
= formData(parameter) formData: FormData
.get[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get)
(method) FormData.get(name: string): FormDataEntryValue | null
("username"); if (usernameconst username: FormDataEntryValue | null
=== "admin") throw redirect(alias) redirect(url: string, init?: number | RouterResponseInit | undefined): never
import redirect
("/admin"); return new Errorvar Error: ErrorConstructor
new (message?: string | undefined) => Error
("Invalid username"); });
export function MyComponentfunction MyComponent(): JSX.Element
() { return (
<form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
action(property) JSX.FormHTMLAttributes<HTMLFormElement>.action?: string | JSX.SerializableAttributeValue | undefined
={isAdminconst isAdmin: Action<[formData: FormData], Error>
} method(property) JSX.FormHTMLAttributes<HTMLFormElement>.method?: JSX.HTMLFormMethod | undefined
="post"> <label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
for(property) JSX.LabelHTMLAttributes<HTMLLabelElement>.for?: string | undefined
="username">Username:</label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="text" name(property) JSX.InputHTMLAttributes<HTMLInputElement>.name?: string | undefined
="username" /> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="submit" value(property) JSX.InputHTMLAttributes<HTMLInputElement>.value?: string | number | string[] | undefined
="submit" /> </form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
> );
}
tsx
import { action(alias) function action<T extends any[], U = void>(fn: (...args: T) => Promise<U>, name?: string): Action<T, U>
import action
, redirect(alias) function redirect(url: string, init?: number | RouterResponseInit): never
import redirect
} from "@solidjs/router";
const isAdminconst isAdmin: Action<[formData: FormData], Error>
= action(alias) action<[formData: FormData], Error>(fn: (formData: FormData) => Promise<Error>, name?: string | undefined): Action<[formData: FormData], Error>
import action
(async (formData(parameter) formData: FormData
: FormDataProvides a way to easily construct a set of key/value pairs representing form fields and their values, which can then be easily sent using the XMLHttpRequest.send() method. It uses the same format a form would use if the encoding type were set to "multipart/form-data".
[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData)
interface FormData
) => { "use server";
await new PromiseCreates a new Promise.
var Promise: PromiseConstructor
new <unknown>(executor: (resolve: (value: unknown) => void, reject: (reason?: any) => void) => void) => Promise<unknown>
((resolve(parameter) resolve: (value: unknown) => void
, reject(parameter) reject: (reason?: any) => void
) => setTimeout[MDN Reference](https://developer.mozilla.org/docs/Web/API/setTimeout)
function setTimeout(handler: TimerHandler, timeout?: number | undefined, ...arguments: any[]): number
(resolve(parameter) resolve: (value: unknown) => void
, 1000)); const usernameconst username: FormDataEntryValue | null
= formData(parameter) formData: FormData
.get[MDN Reference](https://developer.mozilla.org/docs/Web/API/FormData/get)
(method) FormData.get(name: string): FormDataEntryValue | null
("username"); if (usernameconst username: FormDataEntryValue | null
=== "admin") throw redirect(alias) redirect(url: string, init?: number | RouterResponseInit | undefined): never
import redirect
("/admin"); return new Errorvar Error: ErrorConstructor
new (message?: string | undefined) => Error
("Invalid username"); });
export function MyComponentfunction MyComponent(): JSX.Element
() { return (
<form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
action(property) JSX.FormHTMLAttributes<HTMLFormElement>.action?: string | JSX.SerializableAttributeValue | undefined
={isAdminconst isAdmin: Action<[formData: FormData], Error>
} method(property) JSX.FormHTMLAttributes<HTMLFormElement>.method?: JSX.HTMLFormMethod | undefined
="post"> <label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
for(property) JSX.LabelHTMLAttributes<HTMLLabelElement>.for?: string | undefined
="username">Username:</label(property) JSX.HTMLElementTags.label: JSX.LabelHTMLAttributes<HTMLLabelElement>
> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="text" name(property) JSX.InputHTMLAttributes<HTMLInputElement>.name?: string | undefined
="username" /> <input(property) JSX.HTMLElementTags.input: JSX.InputHTMLAttributes<HTMLInputElement>
type(property) JSX.InputHTMLAttributes<HTMLInputElement>.type?: string | undefined
="submit" value(property) JSX.InputHTMLAttributes<HTMLInputElement>.value?: string | number | string[] | undefined
="submit" /> </form(property) JSX.HTMLElementTags.form: JSX.FormHTMLAttributes<HTMLFormElement>
> );
}
Error Handling
We strongly recommend with actions to "return" errors rather than throwing them. This can help with typing of submissions you'd use with useSubmission
. This is important especially for handling progressive enhancement where no JS is present in the client so that we can use the error declaratively to render the updated page on the server.
Additionally when using Server Actions it is good practice to try to handle errors on the server so that you can sanitize error messages.