Skip to main content
Big news! Interval has been acquired by Meter. Learn more →

Linking between actions

Just as you'd use <a> tags to link between HTML pages, you can use Interval's linking APIs to let users navigate between pages and actions. For example, a page might link each row in a table to an "edit" action, or it could conditionally redirect from one action to another.

There are two components to a link: the path, and an optional params object.

info

An action or page's path will generally be its slug. If you're using file-based routing or have Pages with nested actions, the path will be a combination of the parent's path and the action's slug, separated by a /. For example, an action with the slug edit nested under a Page with the slug users will have the path users/edit.

To get a full readout of the routes for all of your actions and pages, visit the All actions screen from your dashboard's Settings menu.

Here's an example of two actions that link to each other:

src/routes/favorite_color.ts
// the path to this action is `favorite_color`

import { Action, io } from "@interval/sdk";

export default new Action(async () => {
await io.group([
io.display.markdown(`Choose your favorite color:`),
io.display.link("Blue", {
route: "result",
params: { favoriteColor: "blue" },
}),
io.display.link("Red", {
route: "result",
params: { favoriteColor: "red" },
}),
]);
});
src/routes/result.ts
// the path to this action is `result`

import { Action, ctx } from "@interval/sdk";

export default new Action(async () => {
await io.group([
io.display.markdown(`Favorite color is: ${ctx.params.favoriteColor}`),
io.display.link("Go back", {
route: "favorite_color",
}),
]);
});

Linking within tables

The most common use of links in Interval apps is to link to actions from within rows in display.table. Here's an example table that links to Edit & Delete actions for each row, and requires an id param in order to run each of them:

src/routes/users/index.ts
import { Page, io } from "@interval/sdk";
import { getUsers } from "../../db";

// the path to this page is "users", and the paths of its nested actions will
// be prefixed with "users/".
export default new Page({
name: "Users",
handler: async () => {
const data = await getUsers();

await io.display.table("Users", {
data,
rowMenuItems: row => [
{
label: "Edit",
route: "users/edit",
params: { id: row.id },
},
{
label: "Delete",
theme: "danger",
route: "users/delete",
params: { id: row.id },
},
],
});
},
});
src/routes/users/edit_user.ts
import { Action, ctx } from "@interval/sdk";
import { getUser } from "../../db";

export default new Action(async () => {
if (!ctx.params.id) {
throw new Error("Missing required param: id");
}

const user = await getUser(ctx.params.id);

// ...
});
src/routes/users/delete_user.ts
import { Action, ctx } from "@interval/sdk";
import { getUser } from "../../db";

export default new Action(async () => {
if (!ctx.params.id) {
throw new Error("Missing required param: id");
}

const user = await getUser(ctx.params.id);

// ...
});
Did this section clearly explain what you wanted to learn?