<!-- ccr-slack-attribution --> _Requested by **Eric Allam** · [Slack thread](https://triggerdotdev.slack.com/archives/C0BEM9Z73TM/p1786528784863449)_ **Before:** you're on the API keys page in project X, you switch to project Y in the sidebar, and you land on project Y's Tasks page. Same for switching organization. Every switch threw away the page you were looking at. **After:** you land on project Y's API keys page. Switching organization does the same thing, one project down. Pages that name a single thing — a run, a batch, a queue, a schedule, a deploy, a session, an error group — can't exist in another project, so those take you to the matching list page instead (a run page takes you to Runs). The environment is still chosen exactly as it is today: nothing tries to guess it in the browser. --- ## Testing - New `apps/webapp/app/utils/pageSwitching.test.ts` (35 tests). It reads the compiled Remix route manifest, so the portable-page list can't silently drift from the routes: - every environment page that names no resource survives an environment switch — the same pages the old slug swap kept - the two branch lists are the only pages an environment switch keeps and a project switch drops - the pages gated per organization — Logs, Query and the queue metrics dashboard — travel with an environment or project switch but not an organization switch, and that list is derived from the route sources so a new gated page cannot be missed - every portable page points at a route that exists - every one of the 19 environment routes that takes a resource id truncates to a list page, with the id gone - portable pages resolve to themselves, so switching twice lands in the same place - every rejection case: leading slash, `//`, absolute URL, `..`, percent-encoded traversal, `javascript:`, unknown page — each falls back to Tasks rather than being sanitised into something - Manual: switch project and organization from API keys, project settings, a run page, and a queue page. - `pnpm run typecheck --filter webapp` passes. - The rest of the webapp suite needs Docker for testcontainers, which wasn't available here; all colocated pure unit tests under `app/utils/` pass (15 files, 150 tests). --- ## Changelog Switching project or organization in the sidebar keeps you on the same page instead of sending you back to Tasks. Pages for a specific run, deploy or other single item open the matching list instead. --- ## How The switcher links already pointed at `/orgs/:org/projects/:project` and `/orgs/:org`, whose `_index` loaders resolve the best environment (and, for the organization, the best project) and redirect. So the page travels as a search param on those links, and each loader appends it to the path it already builds: - `app/utils/pageSwitching.ts` — one pure module. `environmentPortablePage(suffix)` and `projectPortablePage(suffix)` walk up the suffix until they find an entry in an allowlist of portable pages, and answer with the environment root if they find none. The result is therefore always a literal from that closed set, which is what makes it safe to concatenate into a redirect target; there is no regex sanitising. The allowlist is built from the landing pages already listed in `deeplinkPages.ts` plus the handful of nested pages that file doesn't know about, so this isn't a new URL-shape table. - `app/hooks/useEnvironmentSwitcher.ts` — `usePageSwitcher()` derives the current page by slicing the environment layout route match's pathname off the current pathname, so there's no route table on the client either. The query string and hash are dropped on a project or organization switch, since filters encode task slugs and ids scoped to the project you're leaving. - Both `_index` loaders re-validate the page through the same function before using it. Two things worth a look: - **The environment switcher's truncation gap is fixed as a side effect.** It had a hand-written switch covering `runs/:runParam`, `deployments/:deploymentParam` and `schedules/:scheduleParam`; the other 16 id-bearing routes carried their id straight into the new environment (e.g. `queues/:queueParam`, `batches/:batchParam`, `errors/:fingerprint`, `sessions/:sessionParam`). All three switchers now share one truncation, and the test asserts it covers every such route in the manifest. - **Portability turned out to be two properties, not one.** Preview branches and dev branches render under any environment slug of their project — both loaders pass a hardcoded environment type and the project slug and never read `envParam` — so an environment switch keeps them, exactly as swapping the slug did before. A project or organization switch still falls back to Tasks, since the project you land in may have no preview branches. A test locks the environment half: every id-free page below an environment has to survive an environment switch. --- ## Screenshots _n/a — no visual change; only where the switcher links point._ 💯 --------- Co-authored-by: Claude <noreply@anthropic.com>
Build and deploy fully‑managed AI agents and workflows
Website | Docs | Issues | Example projects | Feature requests | Public roadmap | Self-hosting
About Trigger.dev
Trigger.dev is the open-source platform for building AI workflows in TypeScript. Long-running tasks with retries, queues, observability, and elastic scaling.
The platform designed for building AI agents
Build AI agents using all the frameworks, services and LLMs you're used to, deploy them to Trigger.dev and get durable, long-running tasks with retries, queues, observability, and elastic scaling out of the box.
-
Long-running without timeouts: Execute your tasks with absolutely no timeouts, unlike AWS Lambda, Vercel, and other serverless platforms.
-
Durability, retries & queues: Build rock solid agents and AI applications using our durable tasks, retries, queues and idempotency.
-
True runtime freedom: Customize your deployed tasks with system packages – run browsers, Python scripts, FFmpeg and more.
-
Human-in-the-loop: Programmatically pause your tasks until a human can approve, reject or give feedback.
-
Realtime apps & streaming: Move your background jobs to the foreground by subscribing to runs or streaming AI responses to your app.
-
Observability & monitoring: Each run has full tracing and logs. Configure error alerts to catch bugs fast.
Key features:
- JavaScript and TypeScript SDK - Build background tasks using familiar programming models
- Long-running tasks - Handle resource-heavy tasks without timeouts
- Durable cron schedules - Create and attach recurring schedules of up to a year
- Trigger.dev Realtime - Trigger, subscribe to, and get real-time updates for runs, with LLM streaming support
- Build extensions - Hook directly into the build system and customize the build process. Run Python scripts, FFmpeg, browsers, and more.
- React hooks - Interact with the Trigger.dev API on your frontend using our React hooks package
- Batch triggering - Use batchTrigger() to initiate multiple runs of a task with custom payloads and options
- Structured inputs / outputs - Define precise data schemas for your tasks with runtime payload validation
- Waits - Add waits to your tasks to pause execution for a specified duration
- Preview branches - Create isolated environments for testing and development. Integrates with Vercel and git workflows
- Waitpoints - Add human-in-the-loop judgment at critical decision points without disrupting workflow
- Concurrency & queues - Set concurrency rules to manage how multiple tasks execute
- Multiple environments - Support for DEV, PREVIEW, STAGING, and PROD environments
- No infrastructure to manage - Auto-scaling infrastructure that eliminates timeouts and server management
- Automatic retries - If your task encounters an uncaught error, we automatically attempt to run it again
- Checkpointing - Tasks are inherently durable, thanks to our checkpointing feature
- Versioning - Atomic versioning allows you to deploy new versions without affecting running tasks
- Machines - Configure the number of vCPUs and GBs of RAM you want the task to use
- Observability & monitoring - Monitor every aspect of your tasks' performance with comprehensive logging and visualization tools
- Logging & tracing - Comprehensive logging and tracing for all your tasks
- Tags - Attach up to ten tags to each run, allowing you to filter via the dashboard, realtime, and the SDK
- Run metadata - Attach metadata to runs which updates as the run progresses and is available to use in your frontend for live updates
- Bulk actions - Perform actions on multiple runs simultaneously, including replaying and cancelling
- Real-time alerts - Choose your preferred notification method for run failures and deployments
Write tasks in your codebase
Create tasks where they belong: in your codebase. Version control, localhost, test and review like you're already used to.
import { task } from "@trigger.dev/sdk";
//1. You need to export each task
export const helloWorld = task({
//2. Use a unique id for each task
id: "hello-world",
//3. The run function is the main function of the task
run: async (payload: { message: string }) => {
//4. You can write code that runs for a long time here, there are no timeouts
console.log(payload.message);
},
});
Deployment
Use our SDK to write tasks in your codebase. There's no infrastructure to manage, your tasks automatically scale and connect to our cloud. Or you can always self-host.
Environments
We support Development, Staging, Preview, and Production environments, allowing you to test your tasks before deploying them to production.
Full visibility of every job run
View every task in every run so you can tell exactly what happened. We provide a full trace view of every task run so you can see what happened at every step.
Getting started
The quickest way to get started is to create an account and project in our web app, and follow the instructions in the onboarding. Build and deploy your first task in minutes.
Useful links:
- Quick start - get up and running in minutes
- How it works - understand how Trigger.dev works under the hood
- Guides and examples - walk-through guides and code examples for popular frameworks and use cases
Self-hosting
If you prefer to self-host Trigger.dev, you can follow our self-hosting guides:
- Docker self-hosting guide - use Docker Compose to spin up a Trigger.dev instance
- Kubernetes self-hosting guide - use our official Helm chart to deploy Trigger.dev to your Kubernetes cluster
Support and community
We have a large active community in our official Discord server for support, including a dedicated channel for self-hosting.
Development
To setup and develop locally or contribute to the open source project, follow our development guide.

