Concepts

Actions

Edit this page

When developing applications, it is common to need to communicate new information to the server based on user interactions. Actions are Solid Routers solution to this problem.


What are actions?

Actions are asynchronous processing functions that allow you to submit data to your server and receive a response. They are isomorphic, meaning they can run either on the server or the client, depending on what is needed. This flexibility makes actions a powerful tool for managing and tracking data submission.

How actions work

Actions represent the server-side part of an HTML form. They handle submissions through POST requests, allowing you to easily use HTML forms to send data.

When a user performs an action, such as submitting a form, the data is sent to the server for processing via an action.

Benefits of using actions

  1. Isomorphic: Since actions can run on both the server and client, you can optimize performance by choosing the best execution environment for your needs.
  2. Asynchronous processing: Actions handle data submissions asynchronously, ensuring that your application remains responsive.
  3. Simplified data handling: By using actions, the process of managing and tracking data submissions can be streamlined, reducing the complexity of your application.

Creating actions

To create an action, use the action function from the @solidjs/router package. This function takes an asynchronous function as an argument and returns a new function that can be used to submit data.

import { action, useAction } from "@solidjs/router";
const echo = action(async (message: string) => {
// Simulates an asynchronous operation, such as an API call
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
console.log(message);
});

In this example, the echo action simulates a fetch call with a 1 second delay before logging the message to the console. The echo action will act as a backend, however, it can be substituted for any API provided it can be run on the client. Typically, route actions are used with some sort of solution like fetch or GraphQL.

Using actions

To use the action, you can call it from within a component using useAction. This returns a function that can be called with the necessary arguments to trigger the action.

import { useAction } from "@solidjs/router";
export function MyComponent() {
const myEcho = useAction(echo);
myEcho("Hello from Solid!");
}

In this component, useAction is used to get a reference to the echo action. The action is then called with the message "Hello from Solid!", which will be logged to the console after a 1 second delay.

Returning data from actions

In many cases, after submitting data, the server sends some data back as well. This may be in the form of an error message if something has failed or the results of a successful operation. Anything returned from an action can be accessed using the reactive action.result property, where the value can change each time you submit your action.

To access the action's result, you must pass the action to useSubmission:

import { action, useAction, useSubmission } from "@solidjs/router";
const echo = action(async (message: string) => {
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
return message;
});
export function MyComponent() {
const myEcho = useAction(echo);
const echoing = useSubmission(echo);
myEcho("Hello from solid!");
setTimeout(() => myEcho("This is a second submission!"), 1500);
return <p>{echoing.result}</p>;
}

Using useSubmission leaves the implementation details of how you trigger echo up to you. Whe handling user inputs, for example, it is better to use a form for a multitude of reasons.


Using forms to submit data

When submitting data with actions, it is recommended to use HTML forms. These forms can be used prior to JavaScript loading, which creates instantly interactive applications. This also inherently provides accessibility benefits, saving the time of designing a custom UI library that may not have these benefits.

When using forms to submit actions, the first argument passed to your action function is an instance of FormData. To use actions with forms, pass the action to the action property of your form. This creates progressively enhanced forms that work even when JavaScript is disabled.

If you do not return a Response from your action, the user will stay on the same page and responses will be re-triggered. Using a redirect can tell the browser to navigate to a new page.

import { action, redirect } from "@solidjs/router";
const isAdmin = action(async (formData: FormData) => {
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
const username = formData.get("username");
if (username === "admin") throw redirect("/admin");
return new Error("Invalid username");
});
export function MyComponent() {
return (
<form action={isAdmin} method="post">
<label for="username">Username:</label>
<input type="text" name="username" />
<input type="submit" value="submit" />
</form>
);
}

Error handling

Rather than throwing errors, it is recommended to return them from actions. This helps with the typing of submissions that would be used with useSubmission. This is important when handling progressive enhancement where no JavaScript is present in the client, so that errors can be used declaratively to render the updated page on the server.

Additionally, when using server actions, it is good practice to handle errors on the server to sanitize error messages.

Report an issue with this page