Getting Started
Core Architecture
Link Engine
Analytics & Attribution
Partners & Affiliates
Third-Party Integrations
Identity & Security
Automation & Messaging
Developer Tools
The following files were used as context for generating this wiki page:
The Dub project is structured as a scalable pnpm and Turborepo-orchestrated monorepo combining a core Next.js web application with a robust collection of shared internal libraries, compiler configurations, and specialized external micro-apps. By organizing source code into modular workspace packages under dedicated directories, the architecture streamlines multi-tenancy, build pipelines, and publishing workflows across the entire ecosystem.
This layout addresses the complexity of maintaining shared utility infrastructure, robust UI component standards, enterprise routing domains, and client-side embeddable SDKs within a unified codebase. Centralized compiler options and bundler configurations ensure consistent packaging and seamless cross-workspace dependencies.
Sources: packages/ui/package.json:1-133, packages/utils/package.json:1-62, packages/tsconfig/package.json:1-9
The monorepo architecture is governed at the root by pnpm-workspace.yaml, which defines the package discovery topology across application and library directories. The workspace spans four inclusion patterns: apps/*, apps/web/.react-email, packages/*, and packages/embeds/*. Package dependency resolution uses pnpm version 9.15.9 as specified by the packageManager field in the root package.json.
Sources: pnpm-workspace.yaml:1-6, package.json:34-34
The root package.json establishes the monorepo as a private project under the AGPL-3.0-or-later license. It defines global devDependencies including @dub/tailwind-config bound via workspace:*, eslint (^8.48.0), prettier (^3.2.5), prettier-plugin-organize-imports (^3.2.4), prettier-plugin-tailwindcss (^0.6.0), tsconfig bound via workspace:*, and turbo (^1.12.5). A resolutions block pins chrono-node to version 2.7.5.
Sources: package.json:1-35
Note
Root devDependencies such as tsconfig and @dub/tailwind-config use the workspace:* protocol to ensure internal packages resolve directly to their local workspace sources rather than external registries.
Sources: package.json:22-30
The root scripts orchestrate Turborepo pipelines for building, developing, linting, cleaning, and testing, alongside targeted package publishing commands that filter execution by specific workspace identifiers.
Sources: package.json:5-21
Task execution is structured by turbo.json under schema https://turbo.build/schema.json. The pipeline defines global dependencies on any .env file (**/.env) across all tasks. Four primary pipeline targets dictate execution dependencies, caching rules, and output artifacts:
Sources: turbo.json:1-20
Warning
The dev and clean pipeline tasks explicitly disable Turborepo caching ("cache": false). Additionally, dev is marked as persistent ("persistent": true) to support long-running development watcher processes.
Sources: turbo.json:9-15
The monorepo relies on standardized bundler configurations and TypeScript compiler options across its internal packages and applications. Bundling is handled primarily through tsup, leveraging esbuild for high-speed compilation, generation of declaration files (dts), code minification, and conditional workspace clean routines. TypeScript environments are similarly governed by base configuration packages and package-level tsconfig.json files that establish strict type-checking, path aliasing, and module resolution rules.
Packages across the workspace customize tsup to emit specific output formats (esm or cjs), define entry points, handle banner injections, and declare external dependencies such as React.
Sources: packages/utils/tsup.config.ts:1-11, packages/ui/tsup.config.ts:1-20, packages/embeds/core/tsup.config.ts:1-18, packages/cli/tsup.config.ts:1-12
Tip
Both @dub/ui and @dub/embed-core inject a "use client" banner via esbuild options during bundling to ensure consumer frameworks correctly treat their components as client-side modules.
TypeScript settings enforce strict type safety and modular workspace referencing. For instance, packages/cli/tsconfig.json specifies strict mode, Node module resolution, and path aliasing mapping @/* to ./src/*.
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Default",
"compilerOptions": {
"composite": false,
"declaration": true,
"declarationMap": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"inlineSources": false,
"isolatedModules": true,
"moduleResolution": "node",
"noUnusedLocals": false,
"noUnusedParameters": false,
"preserveWatchOutput": true,
"skipLibCheck": true,
"strict": true,
"outDir": "dist",
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}Sources: packages/cli/tsconfig.json:1-26
Application-level configurations, such as apps/web/tsconfig.json, extend shared base configs like tsconfig/nextjs.json and establish comprehensive path aliases for pages, scripts, styles, ui, and libraries alongside explicit inclusions for monorepo packages like packages/blocks/src/event-list.tsx and packages/ui/src/hooks/use-pagination.ts.
{
"extends": "tsconfig/nextjs.json",
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/pages/*": ["pages/*"],
"@/scripts/*": ["scripts/*"],
"@/styles/*": ["styles/*"],
"@/ui/*": ["ui/*"],
"@/lib/*": ["lib/*"]
},
"downlevelIteration": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"strict": false,
"strictNullChecks": true,
"plugins": [
{
"name": "next"
}
]
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
"../../packages/blocks/src/event-list.tsx",
"../../packages/ui/src/hooks/use-pagination.ts"
],
"exclude": ["node_modules", "playwright"]
}Sources: apps/web/tsconfig.json:1-43
Warning
Environment-conditional cleaning (clean: process.env.VERCEL === "1") is used in library packages like packages/utils and packages/ui to optimize Vercel build performance, whereas standalone tools and embeds enforce unconditional directory cleaning (clean: true).
Sources: packages/utils/tsup.config.ts:8-8, packages/ui/tsup.config.ts:17-17, packages/embeds/core/tsup.config.ts:16-16, packages/cli/tsup.config.ts:4-4
The apps/web Next.js application manages its routing layout structure through specialized route groups, dynamic multi-tenant domain segments, and isolated enterprise configurations. Script tasks within apps/web/package.json coordinate generation workflows like Prisma client generation (prisma:generate), concurrent development servers running Next.js with Turbopack on port 8888, openapi generation (generate-openapi), and test runner suites via Vitest and Playwright.
Sources: apps/web/package.json:5-19
Multi-tenant domain routing and marketplace sub-layouts govern how page content is rendered across distinct contexts. The dynamic domain layout (apps/web/app/[domain]/layout.tsx) wraps child components inside a neutral background container bounded by a mobile navigation bar (NavMobile), standard navigation (Nav), and footer components (Footer).
import { Footer, Nav, NavMobile } from "@dub/ui";
export default function ExternalPagesLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<div className="flex min-h-screen flex-col justify-between bg-neutral-50/80">
<NavMobile />
<Nav maxWidthWrapperClassName="max-w-screen-lg lg:px-4 xl:px-0" />
{children}
<Footer className="max-w-screen-lg border-0 bg-transparent lg:px-4 xl:px-0" />
</div>
);
}Sources: apps/web/app/domain/layout.tsx:1-16
Enterprise app routing maps group entry points directly, such as apps/web/app/(ee)/app.dub.co/layout.tsx re-exporting the layout default directly from ../../app.dub.co/layout.
The marketplace section under apps/web/app/app.dub.co/marketplace/layout.tsx integrates decorative external grid lines via MarketplaceExternalGridLines alongside the external marketplace header and footer.
import { MarketplaceExternalHeader } from "@/ui/program-marketplace/external/marketplace-external-header";
import { Footer } from "@dub/ui";
import { PropsWithChildren } from "react";
export default function MarketplaceExternalLayout({
children,
}: PropsWithChildren) {
return (
<div className="flex min-h-screen flex-col bg-white">
<MarketplaceExternalHeader />
<div className="relative flex flex-1 flex-col">
<MarketplaceExternalGridLines />
<main className="flex-1">{children}</main>
<div className="relative z-10 h-px w-full bg-neutral-200" />
<Footer className="border-t-0 md:rounded-t-none" />
</div>
</div>
);
}
function MarketplaceExternalGridLines() {
return (
<div
aria-hidden
className="pointer-events-none absolute inset-0 z-0 flex justify-center"
>
<div className="relative h-full w-full max-w-screen-xl">
<div className="absolute inset-y-0 left-0 w-px bg-neutral-200 [mask-image:linear-gradient(to_bottom,transparent,#000_96px)]" />
<div className="absolute inset-y-0 right-0 w-px bg-neutral-200 [mask-image:linear-gradient(to_bottom,transparent,#000_96px)]" />
</div>
</div>
);
}Dynamic segment matching inside the marketplace is handled explicitly by MarketplaceExternalRouter. The routing flow evaluates path segment lengths and parameter values to select appropriate page views.
import { notFound } from "next/navigation";
import { slugToCategory } from "../utils/urls";
import { MarketplaceExternalHomePage } from "./marketplace-external-home-page";
import { MarketplaceExternalListPage } from "./marketplace-external-list-page";
import { MarketplaceExternalProgramPage } from "./marketplace-external-program-page";
export async function MarketplaceExternalRouter({
segments,
}: {
segments: string[];
}) {
if (segments.length === 0) {
return <MarketplaceExternalHomePage />;
}
if (segments.length === 1 && segments[0] === "all") {
return <MarketplaceExternalListPage segments={segments} />;
}
if (segments.length === 2 && segments[0] === "c") {
const category = slugToCategory(segments[1]);
if (category) {
return (
<MarketplaceExternalListPage
segments={segments}
fixedCategory={category}
/>
);
}
}
if (segments.length === 1) {
return <MarketplaceExternalProgramPage programSlug={segments[0]} />;
}
notFound();
}Tip
The MarketplaceExternalRouter call chain evaluates segments.length === 0 to render the home page before checking explicit filters like all lists, category route prefixes (c), or falling back to single-segment program detail pages or triggering notFound().
The authentication system index module aggregates core administrative, token-hashing, configuration, session, utility, and workspace-level modules under apps/web/lib/auth/index.ts. Specifically, it re-exports modules from ./admin, ./hash-token, ./options, ./session, ./utils, and ./workspace.
export * from "./admin";
export * from "./hash-token";
export * from "./options";
export * from "./session";
export * from "./utils";
export * from "./workspace";Sources: apps/web/lib/auth/index.ts:1-6
Note
The authentication barrel file acts as a single entry point for all sub-auth components, unifying workspace permission checks, session management, and token hashing into a consolidated namespace.
Sources: apps/web/lib/auth/index.ts:1-6
Legacy API routing under apps/web/app/api/(old)/projects/ provides backwards-compatibility proxies that forward historical project-level API routes to modern workspace and domain implementations.
The base projects endpoint re-exports workspace routing handlers directly:
export * from "../../workspaces/route";Similarly, individual project slug routing proxies map project parameter endpoints to parameterized workspace routes:
export * from "../../../workspaces/[idOrSlug]/route";Domain and link info routes under the legacy project hierarchy forward requests to canonical domain and link handlers. The domain collection route exports from the standard domains endpoint:
export * from "../../../../domains/route";Default domain configuration and specific domain routing proxies delegate directly to corresponding domain handlers:
export * from "../../../../../domains/default/route";export * from "../../../../../domains/[domain]/route";Link information routing follows the same delegation pattern, forwarding requests from the legacy project path to canonical link info endpoints:
export * from "../../../../../links/info/route";Sources: apps/web/app/api/old/projects/route.ts:1-1, apps/web/app/api/old/projects/slug/route.ts:1-1, apps/web/app/api/old/projects/slug/domains/route.ts:1-1, apps/web/app/api/old/projects/slug/domains/default/route.ts:1-1, apps/web/app/api/old/projects/slug/domains/domain/route.ts:1-1, apps/web/app/api/old/projects/slug/links/info/route.ts:1-1
The monorepo maintains core shared capabilities under dedicated internal packages in the packages/ directory. These packages abstract common UI primitives, low-level utility functions, and transactional email infrastructure across web applications and micro-apps. Each package is independently versioned, utilizes tsup for compilation to ESM and CJS formats, and declares workspace peer dependencies to ensure consistent React and Next.js runtimes.
Sources: packages/ui/package.json:1-133, packages/email/package.json:1-55, packages/utils/package.json:1-62
@dub/ui)The @dub/ui package provides the design system components, icons, and chart utilities used throughout the Dub interface. It exposes three primary entry points in its exports map: the root package, ./icons, and ./charts.
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
},
"./icons": {
"types": "./dist/icons/index.d.ts",
"import": "./dist/icons/index.mjs",
"require": "./dist/icons/index.js"
},
"./charts": {
"types": "./dist/charts/index.d.ts",
"import": "./dist/charts/index.mjs",
"require": "./dist/charts/index.js"
}
},Sources: packages/ui/package.json:12-28
The package relies heavily on primitive libraries and headless UI components. Its dependency graph includes Floating UI for positioning, Radix UI primitives for accessible overlays and controls, Tiptap extensions for rich-text editing, and Visx packages combined with D3 arrays for charting.
Sources: packages/ui/package.json:57-115
@dub/utils)The @dub/utils package houses shared helper functions and constants exported from a centralized barrel file. Its source index exports all declarations from constants and functions modules:
export * from "./constants";
export * from "./functions";Sources: packages/utils/src/index.ts:1-3
The utility package consumes targeted helper libraries to perform slugification, date parsing, unique ID generation, and class name merging.
Sources: packages/utils/package.json:34-42
@dub/email)The @dub/email package manages transactional email templates and delivery pipelines using React Email and Resend or Nodemailer. It exposes granular exports for root utilities, template files, Resend wrappers, and Nodemailer transport:
"exports": {
".": {
"import": "./src/index.ts",
"require": "./src/index.ts"
},
"./templates/*": {
"import": "./src/templates/*.tsx",
"require": "./src/templates/*.tsx"
},
"./resend": {
"import": "./src/resend/index.ts",
"require": "./src/resend/index.ts"
},
"./resend/*": {
"import": "./src/resend/*.ts",
"require": "./src/resend/*.ts"
},
"./send-via-nodemailer": {
"import": "./src/send-via-nodemailer.ts",
"require": "./src/send-via-nodemailer.ts"
}
}Sources: packages/email/package.json:33-54
Tip
Run pnpm dev inside packages/email to launch the React Email preview server locally on port 3333 targeting ./src/templates.
Sources: packages/email/package.json:6-9
External integrations and embeddable SDKs within the monorepo comprise specialised micro-apps and client-facing packages. These include the HubSpot application package, the Stripe development configuration, and the vanilla JavaScript dashboard embedding core (@dub/embed-core).
Sources: packages/hubspot-app/package.json:1-23, packages/stripe-app/stripe-app.dev.json:1-3, packages/embeds/core/package.json:1-45
@dub/embed-core)The @dub/embed-core package provides a vanilla JavaScript core script for embedding Dub's dashboards into external web pages. Its primary export entry point aggregates constants, core embedding logic, and type definitions through a central barrel file:
export * from "./constants";
export * from "./core";
export * from "./types";The package relies on Floating UI for positioning popups or floating elements within embedded contexts, targeting Node environments alongside browser targets using tsup for bundling:
Note
The @dub/embed-core package is marked with "sideEffects": false to allow aggressive tree-shaking during bundler optimization.
The dub-hubspot-app package configures the HubSpot integration workspace. It specifies a private module configuration requiring Node >=14, wrapping the official HubSpot CLI runner script:
"scripts": {
"hs": "hs"
},
"dependencies": {
"@hubspot/cli": "^7.6.2"
}Similarly, the Stripe integration package (packages/stripe-app) maintains development configuration overriding via its base extension schema:
{
"extends": "stripe-app.json"
}