This document describes the navigation and routing system in the Trigger.dev webapp. It covers:
pathBuilder.ts utility providing type-safe URL generation functionsSideMenu component providing the primary navigation interfaceuseOrganization, useProject, useEnvironment)SelectBestEnvironmentPresenterFor information about the Remix application architecture and route loaders, see Application Architecture. For details about environment management and switching, see Environment Management. For UI component primitives used in navigation, see UI Components and Styling.
Sources: apps/webapp/app/utils/pathBuilder.ts1-504 apps/webapp/app/components/navigation/SideMenu.tsx1-623
The webapp implements a hierarchical URL structure that reflects the multi-tenant nature of Trigger.dev. Every resource is scoped to an organization, project, and environment, creating a nested routing pattern.
Sources: apps/webapp/app/utils/pathBuilder.ts1-504
The routing system uses type-safe parameter types to represent entities in URLs:
| Type | TypeScript Definition | Description | Example |
|---|---|---|---|
OrgForPath | Pick<Organization, "slug"> | Organization identifier | { slug: "acme-corp-a1b2" } |
ProjectForPath | Pick<Project, "slug"> | Project identifier | { slug: "my-project-c3d4" } |
EnvironmentForPath | Pick<RuntimeEnvironment, "slug"> | Environment identifier | { slug: "prod" } or { slug: "dev-user123" } |
v3RunForPath | Pick<TaskRun, "friendlyId"> | Task run identifier | { friendlyId: "run_abc123" } |
v3SpanForPath | Pick<TaskRun, "spanId"> | Span identifier | { spanId: "span_xyz789" } |
DeploymentForPath | Pick<WorkerDeployment, "shortCode"> | Deployment identifier | { shortCode: "deploy_xyz" } |
TaskForPath | { taskIdentifier: string } | Task identifier | { taskIdentifier: "my-task" } |
These types use TypeScript's Pick utility to extract only the slug or identifier field from database models, ensuring path functions receive minimal, focused data.
Sources: apps/webapp/app/utils/pathBuilder.ts1-16
The pathBuilder.ts utility provides type-safe functions for generating URLs throughout the application. All URL generation goes through these functions to ensure consistency and prevent broken links.
Function Hierarchy by Scope:
Sources: apps/webapp/app/utils/pathBuilder.ts51-504
All path functions follow a consistent pattern, building URLs from base paths:
Helper functions extract slug values:
Usage examples:
Sources: apps/webapp/app/utils/pathBuilder.ts92-312 apps/webapp/app/utils/pathBuilder.ts120-137
The system uses Zod schemas to validate URL parameters in route loaders. Each schema extends the previous one, building a hierarchy:
| Schema | Parameters | TypeScript Type | Purpose |
|---|---|---|---|
OrganizationParamsSchema | organizationSlug: string | { organizationSlug: string } | Validates organization routes |
ProjectParamSchema | organizationSlug, projectParam | { organizationSlug: string, projectParam: string } | Validates project routes |
EnvironmentParamSchema | organizationSlug, projectParam, envParam | { organizationSlug: string, projectParam: string, envParam: string } | Validates environment routes |
v3TaskParamsSchema | Above + taskParam | EnvironmentParamSchema & { taskParam: string } | Validates task routes |
v3RunParamsSchema | Above + runParam | EnvironmentParamSchema & { runParam: string } | Validates run detail routes |
v3SpanParamsSchema | Above + spanParam | v3RunParamsSchema & { spanParam: string } | Validates span detail routes |
v3DeploymentParams | Above + deploymentParam | EnvironmentParamSchema & { deploymentParam: string } | Validates deployment routes |
v3ScheduleParams | Above + scheduleParam | EnvironmentParamSchema & { scheduleParam: string } | Validates schedule routes |
Schema definitions:
Usage in route loaders:
Sources: apps/webapp/app/utils/pathBuilder.ts18-49
Organizations and projects use unique slugs in URLs. Slugs are generated from user-provided names with collision detection and retry logic.
The createOrganization function generates slugs using a combination of the user-provided title and a 4-character random suffix:
slug librarynanoid with alphabet 1234567890abcdefSources: apps/webapp/app/models/organization.server.ts18-77
Project slug generation follows the same pattern:
Both use recursive retry logic with an attemptCount parameter that throws an error after 100 attempts, though in practice collisions are extremely rare due to the 4-character suffix (65,536 possible combinations).
Sources: apps/webapp/app/models/project.server.ts19-107
The SideMenu component provides the primary navigation interface, displaying organization/project selection, environment switching, and navigation links for all main sections.
Sources: apps/webapp/app/components/navigation/SideMenu.tsx112-350
The SideMenuItem component renders each navigation link with consistent styling and active state detection:
| Menu Item | Icon | Path Function | Active Color |
|---|---|---|---|
| Tasks | TaskIconSmall | v3EnvironmentPath | text-tasks |
| Runs | RunsIconExtraSmall | v3RunsPath | text-runs |
| Batches | Squares2X2Icon | v3BatchesPath | text-batches |
| Schedules | ClockIcon | v3SchedulesPath | text-schedules |
| Queues | RectangleStackIcon | v3QueuesPath | text-queues |
| Deployments | ServerStackIcon | v3DeploymentsPath | text-deployments |
| Test | BeakerIcon | v3TestPath | text-tests |
Sources: apps/webapp/app/components/navigation/SideMenu.tsx218-266
The ProjectSelector component at the top of the SideMenu provides organization and project switching via a Popover menu.
Component Structure:
Key implementation details:
SwitchOrganizations submenu:
Uses a nested Popover with hover-based opening:
The component features:
useEffect watching navigation.location?.pathnameisManagedCloud feature flagcurrentPlanisSelected prop and FolderOpenIcon vs FolderIconSources: apps/webapp/app/components/navigation/SideMenu.tsx352-597
The SideMenu implements scroll-based header border visibility:
This creates a subtle visual indicator when the menu is scrolled, showing a border under the header section.
Sources: apps/webapp/app/components/navigation/SideMenu.tsx119-138
The webapp uses Remix's route matching system with custom hooks to access loader data throughout the component tree.
The useMatchesData utility searches all matched routes for specific loader data:
This hook:
reduce to short-circuit on first founddebug: trueRemix's useMatches() returns all currently matched routes in the route hierarchy, allowing any component to access data from parent loaders.
Sources: apps/webapp/app/utils.ts44-66
Specialized hooks provide type-safe access to organization, project, and environment data from route loaders.
Hook Overview:
| Hook | Route Match ID | Returns | Throws if Missing |
|---|---|---|---|
useOptionalOrganizations | organizationMatchId | MatchedOrganization[] or undefined | No |
useOrganizations | organizationMatchId | MatchedOrganization[] | Yes (invariant) |
useOptionalOrganization | organizationMatchId | MatchedOrganization or undefined | No |
useOrganization | organizationMatchId | MatchedOrganization | Yes (invariant) |
useOptionalProject | organizationMatchId | MatchedProject or undefined | No |
useProject | organizationMatchId | MatchedProject | Yes (invariant) |
useOptionalEnvironment | organizationMatchId | MatchedEnvironment or undefined | No |
useEnvironment | organizationMatchId | MatchedEnvironment | Yes (invariant) |
useIsImpersonating | organizationMatchId | boolean | No |
Constant definitions:
Hook implementations:
Usage pattern in components:
Change detection hooks:
These hooks enable components to react to organization or project changes via callback functions.
Sources: apps/webapp/app/hooks/useOrganizations.ts1-64 apps/webapp/app/hooks/useProject.tsx1-29
When a user navigates to a project without specifying an environment, the system automatically selects the most appropriate environment using SelectBestEnvironmentPresenter.
Automatic environment redirect:
Route implementation:
The SelectBestEnvironmentPresenter selection logic prioritizes:
orgMember.userId)This ensures users are directed to their personal development environment when available, or to the shared production environment otherwise.
Sources: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam._index/route.tsx1-44 apps/webapp/app/presenters/SelectBestEnvironmentPresenter.server.ts
The AppLayout module provides container components that structure the page layout and work with the navigation system.
Layout Components:
| Component | Purpose | Key Classes |
|---|---|---|
AppContainer | Root container for entire app | grid h-full w-full grid-rows-1 overflow-hidden |
MainBody | Contains page content next to sidebar | grid grid-rows-1 overflow-hidden |
PageContainer | Wrapper for page with header and body | grid-rows-[auto_1fr] overflow-hidden |
PageBody | Scrollable content area | overflow-y-auto scrollbar-thin with padding |
MainCenteredContainer | Centered container for auth/settings pages | mx-auto max-w-xs md:mt-[22vh] |
Sources: apps/webapp/app/components/layout/AppLayout.tsx1-86
The routing system includes protection against open redirect vulnerabilities through the sanitizeRedirectPath utility:
This function ensures that redirect paths:
/ but not //This prevents malicious redirect attacks where an attacker might try to redirect users to external sites via query parameters.
Sources: apps/webapp/app/utils.ts4-42
Refresh this wiki