diff --git a/.github/workflows/lychee.yml b/.github/workflows/lychee.yml index a830bd80af..31b2169180 100644 --- a/.github/workflows/lychee.yml +++ b/.github/workflows/lychee.yml @@ -37,7 +37,7 @@ jobs: with: # prisma.io/extensions ships with PR #8232; drop this exclude once it is live. # /compare/neon, /compare/planetscale, and /compare/supabase ship with PR #8516; drop that exclude once they are live. - # /compare/vercel-neon-drizzle is not live yet; drop that exclude once it is. + # /compare/vercel and /compare/vercel-neon-drizzle are not live yet; drop those excludes once they are. # dashboard.clerk.com/~/ resolves to the signed-in user's application and returns 404 to anonymous requests. args: >- --cache @@ -59,7 +59,7 @@ jobs: --exclude 'https://pris.ly/vercel-app-demo-live' --exclude 'https://app-deploy-demo.prisma.io' --exclude '^https://www\.prisma\.io/extensions' - --exclude '^https://www\.prisma\.io/compare/(neon|planetscale|supabase|vercel-neon-drizzle)$' + --exclude '^https://www\.prisma\.io/compare/(neon|planetscale|supabase|vercel|vercel-neon-drizzle)$' --exclude '^https://dashboard\.clerk\.com/~/' --accept 200,201,204,301,302,303,307,308,403,429 ${{ steps.changed-docs.outputs.all_changed_files }} @@ -95,7 +95,7 @@ jobs: --exclude 'https://pris.ly/vercel-app-demo-live' --exclude 'https://app-deploy-demo.prisma.io' --exclude '^https://www\.prisma\.io/extensions' - --exclude '^https://www\.prisma\.io/compare/(neon|planetscale|supabase|vercel-neon-drizzle)$' + --exclude '^https://www\.prisma\.io/compare/(neon|planetscale|supabase|vercel|vercel-neon-drizzle)$' --exclude '^https://dashboard\.clerk\.com/~/' --accept 200,201,204,301,302,303,307,308,403,429 ${{ steps.changed-docs.outputs.all_changed_files }} diff --git a/apps/site/next.config.mjs b/apps/site/next.config.mjs index 3a0117fd67..f618470c99 100644 --- a/apps/site/next.config.mjs +++ b/apps/site/next.config.mjs @@ -276,6 +276,7 @@ const agentMarkdownPaths = [ "/compare/neon", "/compare/planetscale", "/compare/supabase", + "/compare/vercel", "/compare/vercel-neon-drizzle", ]; diff --git a/apps/site/src/app/compare/article.tsx b/apps/site/src/app/compare/article.tsx index 8f76feb06e..01bc524db5 100644 --- a/apps/site/src/app/compare/article.tsx +++ b/apps/site/src/app/compare/article.tsx @@ -33,6 +33,7 @@ const AI_MARKDOWN: Partial> = { neon: "/compare/neon.md", planetscale: "/compare/planetscale.md", supabase: "/compare/supabase.md", + vercel: "/compare/vercel.md", "vercel-neon-drizzle": "/compare/vercel-neon-drizzle.md", }; @@ -113,6 +114,18 @@ const TOC: Record = { { id: "faq", label: "FAQ", also: ["next"] }, { id: "sources", label: "Sources" }, ], + vercel: [ + { id: "choose", label: "When to choose" }, + { id: "feature-table", label: "Features" }, + { id: "where-each", label: "Where each fits" }, + { id: "apart", label: "Why Prisma" }, + { id: "pricing", label: "Pricing" }, + { id: "wrong-choice", label: "When it is the wrong choice" }, + { id: "migrate", label: "Switching", also: ["migrate-back"] }, + { id: "proof", label: "What teams say" }, + { id: "faq", label: "FAQ", also: ["next"] }, + { id: "sources", label: "Sources" }, + ], "vercel-neon-drizzle": [ { id: "choose", label: "When to choose" }, { id: "feature-table", label: "Features" }, diff --git a/apps/site/src/app/compare/comparisons.ts b/apps/site/src/app/compare/comparisons.ts index 7599f5d51c..6685265868 100644 --- a/apps/site/src/app/compare/comparisons.ts +++ b/apps/site/src/app/compare/comparisons.ts @@ -4,6 +4,7 @@ import { MONGOOSE_DESCRIPTION, MONGOOSE_TITLE } from "./mongoose/_content"; import { NEON_DESCRIPTION, NEON_TITLE } from "./neon/_content"; import { PLANETSCALE_DESCRIPTION, PLANETSCALE_TITLE } from "./planetscale/_content"; import { SUPABASE_DESCRIPTION, SUPABASE_TITLE } from "./supabase/_content"; +import { VERCEL_DESCRIPTION, VERCEL_TITLE } from "./vercel/_content"; import { VERCEL_NEON_DRIZZLE_DESCRIPTION, VERCEL_NEON_DRIZZLE_TITLE, @@ -88,6 +89,13 @@ export const COMPARISONS = [ title: SUPABASE_TITLE, description: SUPABASE_DESCRIPTION, }, + { + slug: "vercel", + category: "compute", + competitor: "Vercel", + title: VERCEL_TITLE, + description: VERCEL_DESCRIPTION, + }, { slug: "vercel-neon-drizzle", category: "platform", diff --git a/apps/site/src/app/compare/page-ai-actions.tsx b/apps/site/src/app/compare/page-ai-actions.tsx index 3fffcb7e19..682c890570 100644 --- a/apps/site/src/app/compare/page-ai-actions.tsx +++ b/apps/site/src/app/compare/page-ai-actions.tsx @@ -53,6 +53,14 @@ const SUPABASE_PROMPT = `I am choosing between Prisma Postgres and Supabase for Ask me up to three short questions if I need Auth/Storage/Realtime out of the box, whether I want the TypeScript app hosted next to Postgres, or whether HIPAA/SOC 2/ISO 27001 matter.`; +const VERCEL_PROMPT = `I am choosing between Prisma Compute and Vercel for hosting a TypeScript app. Using only what the article states, plus my answers: + +1. Tell me which fits if I want Prisma Compute, Prisma Postgres, and Prisma ORM in one first-party platform vs if I want Vercel hosting and a Marketplace database. +2. Call out the real tradeoffs (app next to the database vs Marketplace placement, preview branches vs Vercel previews, operations vs Pro credit plus usage, CDN vs TypeScript HTTP hosting) without selling. +3. Give me the first steps to try the one you recommend. + +Ask me up to three short questions if I already use Vercel, whether I want the TypeScript app hosted next to Postgres, or whether I need a global CDN.`; + const VERCEL_NEON_DRIZZLE_PROMPT = `I am choosing between the Prisma platform and Vercel + Neon + Drizzle for a TypeScript backend. Using only what the article states, plus my answers: 1. Tell me which fits if I want Prisma ORM, Prisma Postgres, and Prisma Compute in one platform vs if I want to wire Drizzle, Neon, and Vercel myself. @@ -73,17 +81,19 @@ Ask me up to three short questions if I need a copy of production data on previe export function choosePrompt(pageMdUrl: string) { const body = pageMdUrl.includes("/compare/vercel-neon-drizzle.md") ? VERCEL_NEON_DRIZZLE_PROMPT - : pageMdUrl.includes("/compare/planetscale.md") - ? PLANETSCALE_PROMPT - : pageMdUrl.includes("/compare/supabase.md") - ? SUPABASE_PROMPT - : pageMdUrl.includes("/compare/neon.md") - ? NEON_PROMPT - : pageMdUrl.includes("/compare/convex.md") - ? CONVEX_PROMPT - : pageMdUrl.includes("/compare/mongoose.md") - ? MONGOOSE_PROMPT - : KYSELY_PROMPT; + : pageMdUrl.includes("/compare/vercel.md") + ? VERCEL_PROMPT + : pageMdUrl.includes("/compare/planetscale.md") + ? PLANETSCALE_PROMPT + : pageMdUrl.includes("/compare/supabase.md") + ? SUPABASE_PROMPT + : pageMdUrl.includes("/compare/neon.md") + ? NEON_PROMPT + : pageMdUrl.includes("/compare/convex.md") + ? CONVEX_PROMPT + : pageMdUrl.includes("/compare/mongoose.md") + ? MONGOOSE_PROMPT + : KYSELY_PROMPT; return `Read this article and treat it as the source of truth: ${pageMdUrl} diff --git a/apps/site/src/app/compare/vercel.json/route.ts b/apps/site/src/app/compare/vercel.json/route.ts new file mode 100644 index 0000000000..7e3cbcc8e2 --- /dev/null +++ b/apps/site/src/app/compare/vercel.json/route.ts @@ -0,0 +1,11 @@ +import { NextResponse } from "next/server"; +import { getVercelFacts } from "../vercel/facts"; + +/** Machine-readable comparison facts, each value with a source URL and dateChecked. */ +export function GET() { + return NextResponse.json(getVercelFacts(), { + headers: { + "Cache-Control": "public, max-age=300, s-maxage=3600", + }, + }); +} diff --git a/apps/site/src/app/compare/vercel/_content.ts b/apps/site/src/app/compare/vercel/_content.ts new file mode 100644 index 0000000000..d906ddce61 --- /dev/null +++ b/apps/site/src/app/compare/vercel/_content.ts @@ -0,0 +1,278 @@ +export const VERCEL_PATH = "/compare/vercel"; + +export const VERCEL_TITLE = "Prisma vs Vercel: app hosting with or without the database?"; + +export const VERCEL_DESCRIPTION = + "Both host TypeScript apps. Pick Prisma Compute when the app should run next to Prisma Postgres and Prisma ORM on one first-party platform. Pick Vercel when you want mature frontend hosting and a Marketplace database. Checked October 2026."; + +export const CHECKED = "October 2026"; + +/** The three-provider stack page. Do not overwrite /compare/vercel-neon-drizzle. */ +export const VERCEL_NEON_DRIZZLE_COMPARE_PATH = "/compare/vercel-neon-drizzle"; + +/** The blog's author page and photo; `/blog` is rewritten to the blog app. */ +export const VERCEL_AUTHOR = { + name: "Martin Janse van Rensburg", + path: "/blog/author/martin-janse-van-rensburg", + image: "/blog/authors/martin-janse-van-rensburg.png", +} as const; + +export const VERCEL_PUBLISHED = { iso: "2026-10-19", label: "October 2026" } as const; +export const VERCEL_UPDATED = { iso: "2026-10-19", label: "October 2026" } as const; + +export const CHOOSE_PRISMA = [ + "You want the app hosted next to [Prisma Postgres](/docs/postgres) in the same project, without a cross-vendor network hop.", + "You want a [preview branch](/docs/compute/branching) that can own its app services and its database.", + "You want [Prisma ORM](/docs/orm), Prisma Postgres, and [Prisma Compute](/docs/compute) as one first-party platform.", + "You want one Prisma workflow across the data model, the database, and deploys.", +]; + +export const CHOOSE_VERCEL = [ + "You want mature frontend hosting, a global CDN, and established preview deployments.", + "You want deep Next.js integration and broad framework support.", + "You want to connect a Marketplace database such as Neon, Supabase, or Prisma Postgres.", + "You want hosting first, and to pick the database and ORM separately.", +]; + +export type CompareRow = { + feature: string; + vercel: string; + prisma: string; + sources: { vercel: string; prisma: string }; +}; + +/** Columns run Feature, Vercel, Prisma. */ +export const COMPARE_ROWS: readonly CompareRow[] = [ + { + feature: "TypeScript app hosting", + vercel: + "Application hosting for frontend and full-stack apps, including Next.js and other frameworks.", + prisma: + "[Prisma Compute](/docs/compute) hosts TypeScript apps (Node.js, Bun, or Next.js) as HTTP services.", + sources: { + vercel: "https://vercel.com/docs/frameworks/nextjs", + prisma: "https://www.prisma.io/docs/compute", + }, + }, + { + feature: "First-party managed Postgres", + vercel: + "Database providers through the Vercel Marketplace, including Neon, Supabase, and Prisma Postgres.", + prisma: + "[Prisma Postgres](/docs/postgres) is first-party managed PostgreSQL in the same project as the app.", + sources: { + vercel: "https://vercel.com/marketplace", + prisma: "https://www.prisma.io/docs/postgres", + }, + }, + { + feature: "First-party type-safe ORM", + vercel: "Bring your own. Vercel does not ship a first-party ORM.", + prisma: + "[Prisma ORM](/docs/orm) is a first-party type-safe ORM on the same platform. It is optional and free to use.", + sources: { + vercel: "https://vercel.com/docs", + prisma: "https://www.prisma.io/docs/orm", + }, + }, + { + feature: "App and database placement", + vercel: "Database placement depends on the Marketplace provider and the deployment region.", + prisma: + "Prisma Compute hosts the app next to Prisma Postgres in the same project, so the app and database do not need a cross-vendor network hop.", + sources: { + vercel: "https://vercel.com/marketplace", + prisma: "https://www.prisma.io/docs/compute", + }, + }, + { + feature: "Preview environments", + vercel: + "Preview deployments for every Git push. A preview database depends on the Marketplace provider.", + prisma: + "A Prisma [preview branch](/docs/compute/branching) can own its Compute services and its Prisma Postgres database. Production data is not copied into that database.", + sources: { + vercel: "https://vercel.com/docs/deployments/environments", + prisma: "https://www.prisma.io/docs/compute/branching", + }, + }, + { + feature: "Preview deployments", + vercel: "Preview deployments for every Git push on Hobby and Pro.", + prisma: + "After deploy-on-push is set up, every pushed branch gets an isolated environment with its own services and, optionally, its own database.", + sources: { + vercel: "https://vercel.com/docs/deployments/environments", + prisma: "https://www.prisma.io/docs/compute", + }, + }, + { + feature: "One first-party workflow", + vercel: + "Hosting is first-party. Database and data-layer tools come through Marketplace integrations or separate products.", + prisma: + "Prisma ORM, Prisma Postgres, and Prisma Compute sit in one first-party workflow, with one CLI and one [REST API](/docs/rest-api).", + sources: { + vercel: "https://vercel.com/marketplace", + prisma: "https://www.prisma.io/docs/compute", + }, + }, + { + feature: "Mature frontend hosting and CDN", + vercel: "Global CDN, Image Optimization, and a mature frontend hosting ecosystem.", + prisma: + "Prisma Compute focuses on TypeScript HTTP services next to Prisma Postgres. It is not a general CDN platform. WebSocket servers are not supported. Compute has no built-in cron scheduler.", + sources: { + vercel: "https://vercel.com/docs/cdn", + prisma: "https://www.prisma.io/docs/compute/limitations", + }, + }, +]; + +export const VERCEL_FITS = [ + "Mature hosting for frontend and full-stack applications, with a global CDN.", + "Deep Next.js integration and broad framework support.", + "Established preview deployments for every Git push.", + "A Marketplace of database and infrastructure integrations, including Prisma Postgres.", +]; + +export const PRISMA_FITS = [ + "Prisma Compute and Prisma Postgres run as one first-party platform.", + "A preview branch can own its app services and its database.", + "The app can sit next to Prisma Postgres in the same project, without a cross-vendor network hop.", + "Prisma ORM connects the typed data layer to the same workflow. It is optional and free to use.", +]; + +export const APART = [ + { + id: "hosting-and-database", + title: "Hosting and database built together", + body: "Prisma Compute and Prisma Postgres are designed and operated as one platform. Vercel connects application hosting to databases from Marketplace providers.", + }, + { + id: "co-located", + title: "App and database next to each other", + body: "Prisma Compute hosts the app next to Prisma Postgres in the same project, so the path does not cross vendors. On Vercel, database placement depends on the provider and the integration.", + }, + { + id: "one-workflow", + title: "One integrated workflow", + body: "The app, the database, the data model, and preview branches work through the same Prisma platform. On Vercel, application hosting is first-party; the database and the data layer come through separate products or integrations.", + }, + { + id: "type-safe-data-layer", + title: "A first-party type-safe data layer", + body: "Prisma ORM is part of the same platform. It is free to use, and free and open source, and always will be. On Vercel, the ORM and data-access tooling are chosen separately.", + }, +]; + +export const PRICING: readonly { + label: string; + vercel: string; + prisma: string; + sources: { vercel: string; prisma: string }; +}[] = [ + { + label: "Free plan", + vercel: + "Hobby is $0 a month for personal, non-commercial use. Included usage is capped; extra usage cannot be purchased.", + prisma: + "Free is $0. No credit card required. Prisma Compute includes 1M requests, 360 GB-hours of memory, 4 active vCPU-hours, and 10 GB outbound bandwidth a month. Prisma Postgres includes 200,000 operations, 2 GB of storage, and 50 databases.", + sources: { + vercel: "https://vercel.com/pricing", + prisma: "https://www.prisma.io/pricing", + }, + }, + { + label: "Paid plans", + vercel: + "Pro is $20 a month for one deploying seat plus usage, and includes $20 in monthly credit. Extra deploying seats are $20 a month each. Usage beyond the credit is billed on demand.", + prisma: + "Starter $10, Pro $49, Business $129 a month for Prisma Postgres and Prisma Compute, with spend limits on paid plans.", + sources: { + vercel: "https://vercel.com/docs/plans/pro-plan", + prisma: "https://www.prisma.io/pricing", + }, + }, + { + label: "Database", + vercel: "Database pricing depends on the Marketplace provider you connect.", + prisma: + "Prisma Postgres sits on the same plans. Free includes 200,000 operations a month, 2 GB of storage, and 50 databases.", + sources: { + vercel: "https://vercel.com/marketplace", + prisma: "https://www.prisma.io/pricing", + }, + }, + { + label: "ORM", + vercel: "Bring your own. Vercel does not ship a first-party ORM, so there is no ORM bill.", + prisma: "Prisma ORM is free and open source, and always will be.", + sources: { + vercel: "https://vercel.com/docs", + prisma: "https://www.prisma.io/docs/orm", + }, + }, + { + label: "Spend controls", + vercel: + "Spend Management on Pro, with a default $200 on-demand usage budget and optional hard limits that can pause projects.", + prisma: "Hard spend limits on paid plans. The Free plan has no usage billing.", + sources: { + vercel: "https://vercel.com/docs/plans/pro-plan", + prisma: "https://www.prisma.io/pricing", + }, + }, +]; + +export const WRONG_CHOICE = [ + "You want a mature frontend CDN, Image Optimization, and the Vercel hosting ecosystem. Prisma Compute hosts TypeScript HTTP services next to Prisma Postgres. It is not a general CDN platform.", + "You want hosting and the database managed from the Vercel dashboard. Prisma Postgres works with an app on Vercel, but it is managed from the Prisma Console.", + "You want to pick the database and ORM separately and connect them yourself. See [Prisma vs Vercel + Neon + Drizzle](/compare/vercel-neon-drizzle) when that stack is the alternative.", + "You need WebSocket servers or a built-in cron scheduler. Prisma Compute does not support those today. See [Compute limitations](/docs/compute/limitations).", +]; + +export const FAQ: readonly { question: string; answer: string }[] = [ + { + question: "Can I keep Vercel and use Prisma Postgres?", + answer: + "Yes. Prisma Postgres works with any Postgres client, including an app you host on Vercel. Prisma Compute is optional app hosting in the same Prisma project. Source: [Prisma Postgres](/docs/postgres) and [Prisma Compute](/docs/compute), October 2026.", + }, + { + question: "Do I need Prisma ORM to use Prisma Compute?", + answer: + "No. Prisma Compute runs TypeScript apps that do not use Prisma ORM. Prisma Postgres also works with any PostgreSQL client, so Prisma ORM is optional on both products. Source: [Prisma Compute](/docs/compute), October 2026.", + }, + { + question: "How do Prisma and Vercel price hosting?", + answer: + "Vercel Hobby is $0 for personal, non-commercial use. Pro is $20 a month for one deploying seat plus usage, and includes $20 in monthly credit. Extra deploying seats are $20 a month each. Prisma Free is $0 with no credit card: Prisma Compute includes 1M requests, 360 GB-hours of memory, 4 active vCPU-hours, and 10 GB outbound bandwidth a month; Prisma Postgres includes 200,000 operations, 2 GB of storage, and 50 databases. Paid Prisma plans are Starter $10, Pro $49, and Business $129 a month, with spend limits. Prisma ORM is free and open source, and always will be. Source: [Vercel pricing](https://vercel.com/pricing) and [Prisma pricing](/pricing), October 2026.", + }, + { + question: "Does Prisma Compute include a CDN like Vercel?", + answer: + "No. Prisma Compute hosts TypeScript HTTP services next to Prisma Postgres. It is not a general CDN platform. Vercel includes a global CDN and Image Optimization. Source: [Compute limitations](/docs/compute/limitations) and [Vercel CDN](https://vercel.com/docs/cdn), October 2026.", + }, + { + question: "How is this different from Prisma vs Vercel + Neon + Drizzle?", + answer: + "This page compares Prisma Compute, Prisma Postgres, and Prisma ORM with Vercel as a host. The [Vercel + Neon + Drizzle](/compare/vercel-neon-drizzle) page compares the same Prisma platform with a three-provider stack: Vercel for hosting, Neon for Postgres, and Drizzle for the ORM. Source: this page and [Prisma vs Vercel + Neon + Drizzle](/compare/vercel-neon-drizzle), October 2026.", + }, +]; + +/** Vercel is a hosted service, so it carries no license or code repository. */ +export const TOOLS = { + vercel: { + name: "Vercel", + softwareVersion: "current", + url: "https://vercel.com", + }, + prisma: { + name: "Prisma", + softwareVersion: "current", + license: "https://www.apache.org/licenses/LICENSE-2.0", + codeRepository: "https://github.com/prisma/prisma", + programmingLanguage: "TypeScript", + url: "https://www.prisma.io", + }, +} as const; diff --git a/apps/site/src/app/compare/vercel/_sections.tsx b/apps/site/src/app/compare/vercel/_sections.tsx new file mode 100644 index 0000000000..7512865842 --- /dev/null +++ b/apps/site/src/app/compare/vercel/_sections.tsx @@ -0,0 +1,427 @@ +import Link from "next/link"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { TESTIMONIALS } from "@/components/sections/testimonials-data"; +import { AnchorHeading } from "../anchor-heading"; +import { + CompareArticle, + comparisonChooseTable, + comparisonClosingCta, + comparisonDisclosure, + comparisonHeroCtas, +} from "../article"; +import { comparisonByline } from "../byline"; +import { comparisonProofCards } from "../proof-cards"; +import { articleReadingStats } from "../reading-time"; +import { CompareTable } from "../compare-table"; +import { ComparisonPricingTable } from "../pricing-table"; +import { QUIET_LINK } from "../table-styles"; +import { renderFaqAnswer } from "@/lib/faq-answer"; +import { + APART, + CHECKED, + CHOOSE_PRISMA, + CHOOSE_VERCEL, + COMPARE_ROWS, + FAQ, + PRICING, + PRISMA_FITS, + VERCEL_AUTHOR, + VERCEL_FITS, + VERCEL_NEON_DRIZZLE_COMPARE_PATH, + VERCEL_TITLE, + VERCEL_UPDATED, + WRONG_CHOICE, +} from "./_content"; + +const INLINE = + "font-medium text-foreground underline underline-offset-4 decoration-black/35 transition-colors hover:decoration-black/60"; + +/** + * One rhythm for the whole body: 56px between sections and 20px under every h2. + * Prose margins are zeroed so these gaps are the only spacing, and every + * paragraph and list item shares one size, line height and colour. Code spans from prose and from + * `renderFaqAnswer` share one treatment, and a span that wraps keeps its padding on both lines. + */ +const BODY = + "flex flex-col gap-14 [&_:is(h2,h3)]:my-0 [&_:is(h2,h3)]:text-primary [&_:is(p,ul,ol,li,table,figure,img)]:my-0 [&_:is(p,li)]:text-base [&_:is(p,li)]:leading-7 [&_:is(p,li)]:text-foreground [&_code]:box-decoration-clone [&_code]:rounded [&_code]:border-0 [&_code]:bg-black/[0.045] [&_code]:px-1 [&_code]:py-0.5 [&_code]:text-[0.875em] [&_code]:font-[family-name:var(--font-code)] [&_code]:font-medium [&_code]:text-primary [&_code]:before:content-none [&_code]:after:content-none [&_table_code]:text-[13px] [&_blockquote]:my-0 [&_blockquote]:border-0 [&_blockquote]:pl-0 [&_blockquote]:font-normal [&_blockquote]:not-italic [&_blockquote_p]:before:content-none [&_blockquote_p]:after:content-none"; +const SECTION = "flex flex-col gap-5"; +const LIST = "list-disc space-y-2 pl-5 marker:text-black/25"; +const STEPS = "list-decimal space-y-2 pl-5 marker:text-muted-foreground"; +const CHOOSE_LABEL = "font-heading text-lg leading-snug font-medium"; + +const PROOF = TESTIMONIALS.filter( + (item) => item.name === "Ron Cohen" || item.name === "Omar López", +); + +const LOGOS: Record = { + Bucket: { src: "/logos/customers/bucket.png", width: 108 }, + "Cal.com": { src: "/logos/companies/cal.svg", width: 96 }, +}; + +const SOURCES = [ + { + product: "Vercel", + links: [ + { label: "Vercel pricing", href: "https://vercel.com/pricing" }, + { label: "Vercel Pro plan", href: "https://vercel.com/docs/plans/pro-plan" }, + { label: "Vercel plans", href: "https://vercel.com/docs/plans" }, + { label: "Vercel Marketplace", href: "https://vercel.com/marketplace" }, + { + label: "Vercel deployment environments", + href: "https://vercel.com/docs/deployments/environments", + }, + { label: "Vercel CDN", href: "https://vercel.com/docs/cdn" }, + { label: "Next.js on Vercel", href: "https://vercel.com/docs/frameworks/nextjs" }, + ], + }, + { + product: "Prisma Compute, Prisma Postgres, and Prisma ORM", + links: [ + { label: "Prisma Compute", href: "/docs/compute" }, + { label: "Prisma Postgres", href: "/docs/postgres" }, + { label: "Prisma ORM docs", href: "/docs/orm" }, + { label: "Prisma REST API", href: "/docs/rest-api" }, + { label: "Compute limitations", href: "/docs/compute/limitations" }, + { label: "Compute branching", href: "/docs/compute/branching" }, + { label: "Prisma vs Vercel + Neon + Drizzle", href: VERCEL_NEON_DRIZZLE_COMPARE_PATH }, + { + label: "Import from an existing database", + href: "/docs/postgres/import-from-existing-database", + }, + { label: "Apache License 2.0", href: "https://github.com/prisma/prisma/blob/main/LICENSE" }, + { label: "Prisma pricing", href: "/pricing" }, + ], + }, +] as const; + +function SourceLink({ href, children }: { href: string; children: string }) { + return href.startsWith("/") ? ( + + {children} + + ) : ( + + {children} + + ); +} + +/** Build the shared content tree for the HTML page and its Markdown rendition. */ +function buildVercelPage(minutes: number) { + return [ +
+

+ {VERCEL_TITLE} +

+

+ Both host TypeScript apps.{" "} + + Vercel + {" "} + connects application hosting to databases through Marketplace integrations.{" "} + + Prisma Compute + {" "} + runs the app next to{" "} + + Prisma Postgres + {" "} + as one first-party platform, with{" "} + + Prisma ORM + {" "} + available on the same workflow. Pick Prisma when you want the app and database in one + project, without a cross-vendor network hop. Pick Vercel when you want mature frontend + hosting, a global CDN, and to choose the database from the Marketplace. +

+ {comparisonHeroCtas("postgres", { + secondary: false, + primaryLabel: "Get started with Prisma", + })} + {comparisonByline({ + author: VERCEL_AUTHOR, + updated: VERCEL_UPDATED, + minutes, + })} + {comparisonDisclosure({ + competitor: "Vercel", + checked: CHECKED, + prismaProduct: "Prisma Compute, Prisma Postgres, and Prisma ORM", + })} +
, +
+
+ + When should I choose Vercel or Prisma? + + {comparisonChooseTable({ + competitor: "Vercel", + prisma: "Prisma", + competitorItems: CHOOSE_VERCEL, + prismaItems: CHOOSE_PRISMA, + })} +
+ +
+ + How do Vercel and Prisma compare? + +

+ Both host TypeScript apps. Vercel connects hosting to database providers through + integrations. Prisma Compute, Prisma Postgres, and Prisma ORM sit in one first-party + platform. +

+ ({ + label: row.feature, + cells: [renderFaqAnswer(row.vercel), renderFaqAnswer(row.prisma)], + }))} + /> +
+ +
+ + Where does each one fit? + +
+
+

Vercel

+

+ Vercel is a mature application platform with deep framework support, preview + deployments, and a broad database marketplace. Teams can connect providers such as + Neon, Supabase, and Prisma Postgres directly to Vercel projects. +

+
    + {VERCEL_FITS.map((item) => ( +
  • {item}
  • + ))} +
+
+
+

Prisma

+

+ Prisma takes a first-party approach. Prisma Compute runs alongside Prisma Postgres, so + the app and database can deploy and operate together without a cross-vendor network + hop. Prisma ORM adds the typed data layer. It is optional. +

+
    + {PRISMA_FITS.map((item) => ( +
  • {item}
  • + ))} +
+
+
+
+ +
+ + Where Prisma differs from Vercel + +
+ {APART.map((item) => ( +
+ + {item.title} + +

{renderFaqAnswer(item.body)}

+
+ ))} +
+
+ +
+ + How much do Vercel and Prisma cost? + + ({ + label: row.label, + cells: [renderFaqAnswer(row.vercel), renderFaqAnswer(row.prisma)], + }))} + note={ + <> + Prisma Compute and Prisma Postgres share one set of plans. A database on Vercel is + priced by the Marketplace provider. Prisma ORM is free and open source, and always + will be. Prisma Free is $0 a month: 1M Compute requests, 200,000 Postgres operations, + 2 GB of storage, and 50 databases. No credit card required. The{" "} + + pricing page + {" "} + lists the paid plans. Vercel Hobby is $0 for personal use. Pro is $20 a month plus + usage, with a $20 monthly credit ( + + Vercel pricing + + , {CHECKED}). + + } + /> +
+ +
+ + When is Prisma the wrong choice? + +
    + {WRONG_CHOICE.map((item) => ( +
  • {renderFaqAnswer(item)}
  • + ))} +
+
+ +
+ + How do I switch from Vercel to Prisma? + +
    +
  1. + Create a Prisma Postgres database if the app does not already use one. The{" "} + + import from an existing database + {" "} + guide uses pg_dump and pg_restore over the direct connection. +
  2. +
  3. + Point the app at the Prisma Postgres connection string. Prisma ORM is optional; any + Postgres client works. +
  4. +
  5. + Host the HTTP app on{" "} + + Prisma Compute + + , or keep Vercel if that host still fits. +
  6. +
+
+ +
+ + How do I switch from Prisma to Vercel? + +
    +
  1. + Deploy the app on Vercel. Preview deployments run for every Git push. See{" "} + + Vercel deployment environments + + . +
  2. +
  3. + Connect a Marketplace database, or keep Prisma Postgres and point the Vercel app at its + connection string. Prisma Postgres works with any Postgres client. +
  4. +
  5. + Pick an ORM if you want one. Prisma ORM stays optional. For the Vercel + Neon + Drizzle + stack, see{" "} + + Prisma vs Vercel + Neon + Drizzle + + . +
  6. +
+
+ +
+ + What do teams say about Prisma? + +

Two teams on what Prisma does for them.

+ {comparisonProofCards( + PROOF.map((item) => ({ + name: item.name, + role: item.role, + company: item.company, + quote: item.quote, + logo: LOGOS[item.company], + })), + )} +
+ +
+ + Frequently asked questions + + + {FAQ.map((faq, index) => ( + + + {faq.question} + + + {renderFaqAnswer(faq.answer)} + + + ))} + +
+
, + comparisonClosingCta("postgres", { + instantDatabase: false, + secondary: false, + firstCheck: "Get started with Prisma and start building in minutes", + }), +
+

+ Sources +

+

+ Every competitor figure on this page comes from these pages, checked {CHECKED}. +

+
+ {SOURCES.map((group) => ( +
+

{group.product}

+
    + {group.links.map((link) => ( +
  • + {link.label} +
  • + ))} +
+
+ ))} +
+
, + ]; +} + +const VERCEL_READING = articleReadingStats(buildVercelPage(1)); + +export function getVercelReadingStats() { + return articleReadingStats(buildVercelPage(VERCEL_READING.minutes)); +} + +/** Build the shared content tree for the HTML page and its Markdown rendition. */ +export function getVercelPageContent() { + return buildVercelPage(VERCEL_READING.minutes); +} + +/** Render the comparison in the guide article frame. */ +export function VercelPage() { + return {getVercelPageContent()}; +} diff --git a/apps/site/src/app/compare/vercel/facts.ts b/apps/site/src/app/compare/vercel/facts.ts new file mode 100644 index 0000000000..52463a7752 --- /dev/null +++ b/apps/site/src/app/compare/vercel/facts.ts @@ -0,0 +1,83 @@ +import { + COMPARE_ROWS, + PRICING, + TOOLS, + VERCEL_PATH, + VERCEL_PUBLISHED, + VERCEL_UPDATED, +} from "./_content"; +import { getVercelReadingStats } from "./_sections"; + +export type FactValue = { + value: string | null; + source: string; + dateChecked: string; +}; + +/** Machine-readable feature and pricing cells with a source on every value. */ +export function getVercelFacts() { + const dateChecked = VERCEL_UPDATED.iso; + return { + path: VERCEL_PATH, + dateChecked, + tools: TOOLS, + features: COMPARE_ROWS.map((row) => ({ + feature: row.feature, + vercel: { + value: row.vercel, + source: row.sources.vercel, + dateChecked, + } satisfies FactValue, + prisma: { + value: row.prisma, + source: row.sources.prisma, + dateChecked, + } satisfies FactValue, + })), + pricing: PRICING.map((row) => ({ + label: row.label, + vercel: { + value: row.vercel, + source: row.sources.vercel, + dateChecked, + } satisfies FactValue, + prisma: { + value: row.prisma, + source: row.sources.prisma, + dateChecked, + } satisfies FactValue, + })), + }; +} + +export function getVercelAboutEntities() { + return [ + { + "@type": "SoftwareApplication", + name: TOOLS.prisma.name, + softwareVersion: TOOLS.prisma.softwareVersion, + license: TOOLS.prisma.license, + codeRepository: TOOLS.prisma.codeRepository, + programmingLanguage: TOOLS.prisma.programmingLanguage, + url: TOOLS.prisma.url, + }, + { + "@type": "SoftwareApplication", + name: TOOLS.vercel.name, + softwareVersion: TOOLS.vercel.softwareVersion, + url: TOOLS.vercel.url, + }, + ]; +} + +/** Article JSON-LD fields that sit on top of the shared comparison graph. */ +export function getVercelArticleFields() { + const { wordCount, timeRequired } = getVercelReadingStats(); + return { + datePublished: VERCEL_PUBLISHED.iso, + dateModified: VERCEL_UPDATED.iso, + wordCount, + timeRequired, + about: getVercelAboutEntities(), + }; +} diff --git a/apps/site/src/app/compare/vercel/page.tsx b/apps/site/src/app/compare/vercel/page.tsx new file mode 100644 index 0000000000..6266eadbe7 --- /dev/null +++ b/apps/site/src/app/compare/vercel/page.tsx @@ -0,0 +1,59 @@ +import localFont from "next/font/local"; +import { JsonLd } from "@prisma-docs/ui/components/json-ld"; +import { + getComparisonMetadata, + getComparisonOgImageUrl, + getComparisonStructuredData, +} from "@/lib/comparison-metadata"; +import { createFaqStructuredData } from "@/lib/structured-data"; +import { getBaseUrl } from "@/lib/url"; +import { comparisonAuthorPerson } from "../byline"; +import { FAQ, VERCEL_AUTHOR, VERCEL_PATH, VERCEL_TITLE } from "./_content"; +import { getVercelArticleFields } from "./facts"; +import { VercelPage } from "./_sections"; + +/** + * The docs' code face. Shared with the Kysely comparison so both pages load + * one copy of the unbracketed Mona Sans Mono file. + */ +const monaSansMono = localFont({ + src: "../kysely/mona-sans-mono.woff2", + variable: "--font-code", + display: "swap", + weight: "200 900", +}); + +export const metadata = getComparisonMetadata("vercel"); + +const faqStructuredData = createFaqStructuredData(VERCEL_PATH, [...FAQ], VERCEL_TITLE); + +/** The shared comparison graph, with this page's author, dates, image, and tool entities. */ +function articleStructuredData() { + const base = getComparisonStructuredData("vercel"); + const [article, ...rest] = base["@graph"]; + return { + ...base, + "@graph": [ + { + ...article, + ...getVercelArticleFields(), + image: getComparisonOgImageUrl("vercel"), + author: comparisonAuthorPerson(VERCEL_AUTHOR, getBaseUrl()), + }, + ...rest, + ], + }; +} + +/** Render the comparison and its article, breadcrumb and FAQ structured data. */ +export default function Page() { + return ( + <> + + +
+ +
+ + ); +} diff --git a/apps/site/src/components/sections/compare-links-data.ts b/apps/site/src/components/sections/compare-links-data.ts index 0cc8e7965c..279d6c49f0 100644 --- a/apps/site/src/components/sections/compare-links-data.ts +++ b/apps/site/src/components/sections/compare-links-data.ts @@ -59,6 +59,12 @@ export const COMPARE_PRISMA_LINKS: CompareLink[] = [ description: "Managed PostgreSQL compared: optional Prisma ORM and Prisma Compute in one project, or Auth, Storage, Realtime, and Edge Functions.", }, + { + label: "Prisma vs Vercel", + href: "/compare/vercel", + description: + "Prisma Compute, Prisma Postgres, and Prisma ORM compared with Vercel application hosting.", + }, { label: "Prisma vs Vercel + Neon + Drizzle", href: "/compare/vercel-neon-drizzle", diff --git a/apps/site/src/lib/comparison-metadata.ts b/apps/site/src/lib/comparison-metadata.ts index c05722bddc..7f9ca4ba7f 100644 --- a/apps/site/src/lib/comparison-metadata.ts +++ b/apps/site/src/lib/comparison-metadata.ts @@ -24,6 +24,7 @@ export function getComparisonMetadata(slug: ComparisonSlug) { slug === "neon" || slug === "planetscale" || slug === "supabase" || + slug === "vercel" || slug === "vercel-neon-drizzle" ) { types["application/json"] = new URL(`${page.path}.json`, baseUrl).toString(); diff --git a/apps/site/src/lib/markdown/comparisons.test.tsx b/apps/site/src/lib/markdown/comparisons.test.tsx index 3d2e80a49b..dc76c38d3b 100644 --- a/apps/site/src/lib/markdown/comparisons.test.tsx +++ b/apps/site/src/lib/markdown/comparisons.test.tsx @@ -22,6 +22,10 @@ import * as supabase from "@/app/compare/supabase/_content"; import { getSupabaseArticleFields, getSupabaseFacts } from "@/app/compare/supabase/facts"; import { GET as getSupabaseFactsResponse } from "@/app/compare/supabase.json/route"; import { getSupabaseReadingStats } from "@/app/compare/supabase/_sections"; +import * as vercel from "@/app/compare/vercel/_content"; +import { getVercelArticleFields, getVercelFacts } from "@/app/compare/vercel/facts"; +import { GET as getVercelFactsResponse } from "@/app/compare/vercel.json/route"; +import { getVercelReadingStats } from "@/app/compare/vercel/_sections"; import * as vercelNeonDrizzle from "@/app/compare/vercel-neon-drizzle/_content"; import { getVercelNeonDrizzleArticleFields, @@ -75,6 +79,7 @@ for (const entry of COMPARISONS) { neon, planetscale, supabase, + vercel, "vercel-neon-drizzle": vercelNeonDrizzle, }[entry.slug]; const page = getMarkdownPage(`/compare/${entry.slug}`); @@ -420,6 +425,21 @@ test("kysely Markdown keeps the 500K Prisma ORM figure in the closing copy", () planetscaleMarkdown, /Need a database\? \[Create an instant database\]\(https:\/\/create-db\.prisma\.io\/web\)/, ); + const vercelPage = getMarkdownPage("/compare/vercel"); + assert.ok(vercelPage); + const vercelMarkdown = renderMarkdownDocument(vercelPage); + assert.ok(vercelMarkdown.includes("Free to start, no credit card required.")); + assert.ok(vercelMarkdown.includes("Trusted by 500K+ developers globally")); + assert.ok(vercelMarkdown.includes("Prisma ORM is free and open source, and always will be.")); + assert.ok(vercelMarkdown.includes("[Get started with Prisma](https://console.prisma.io)")); + assert.doesNotMatch(vercelMarkdown, /Get started with Prisma Postgres/); + assert.ok(vercelMarkdown.includes("Get started with Prisma and start building in minutes")); + assert.ok(vercelMarkdown.includes("Read the docs for guides and API reference")); + assert.ok(vercelMarkdown.includes("[Get started free](https://console.prisma.io)")); + assert.doesNotMatch(vercelMarkdown, /Create a database and start building in minutes/); + assert.doesNotMatch(vercelMarkdown, /create-db\.prisma\.io/); + assert.doesNotMatch(vercelMarkdown, /Create an instant database/); + assert.doesNotMatch(vercelMarkdown, /\[Talk to us\]/); const vercelNeonDrizzlePage = getMarkdownPage("/compare/vercel-neon-drizzle"); assert.ok(vercelNeonDrizzlePage); const vercelNeonDrizzleMarkdown = renderMarkdownDocument(vercelNeonDrizzlePage); @@ -1523,3 +1543,116 @@ test("vercel-neon-drizzle metadata advertises Markdown and JSON alternates", () assert.match(types["text/markdown"] ?? "", /\/compare\/vercel-neon-drizzle\.md$/); assert.match(types["application/json"] ?? "", /\/compare\/vercel-neon-drizzle\.json$/); }); + +test("Vercel Markdown carries the byline, disclosure, and Sources on the shared chrome", () => { + const page = getMarkdownPage("/compare/vercel"); + assert.ok(page); + const markdown = renderMarkdownDocument(page); + const minutes = getVercelReadingStats().minutes; + assert.ok( + markdown.includes( + `By [${vercel.VERCEL_AUTHOR.name}](${vercel.VERCEL_AUTHOR.path}) · Updated ${vercel.VERCEL_UPDATED.label} · ${minutes} min read`, + ), + ); + for (const social of MARTIN_SOCIALS) { + assert.ok( + markdown.includes(`[${social.label}](${social.href})`), + `missing ${social.label} profile`, + ); + } + assert.ok(markdown.includes("Checked October 2026.")); + assert.ok(markdown.includes(`dateModified: ${vercel.VERCEL_UPDATED.iso}`)); + assert.ok(markdown.includes("Prisma makes Prisma Compute, Prisma Postgres, and Prisma ORM")); + assert.ok(markdown.includes("\n## Sources\n")); + assert.ok(markdown.includes("\n## When should I choose Vercel or Prisma?\n")); + const choose = markdown.slice( + markdown.indexOf("## When should I choose Vercel or Prisma?"), + markdown.indexOf("## How do Vercel and Prisma compare?"), + ); + assert.match(choose, /^\| Vercel \| Prisma \|$/m); + assert.doesNotMatch(choose, /### Vercel/); + assert.ok(markdown.includes("\n## How do Vercel and Prisma compare?\n")); + assert.ok(markdown.includes("\n## Where does each one fit?\n")); + assert.ok(markdown.includes("\n## How much do Vercel and Prisma cost?\n")); + assert.ok(markdown.includes("| Feature | Vercel | Prisma |")); + assert.ok(markdown.includes("| Cost | Vercel | Prisma |")); + assert.ok(markdown.includes("1M")); + assert.ok(markdown.includes("$20")); + assert.ok(markdown.includes("200,000")); + assert.ok(markdown.includes("2 GB")); + assert.ok(markdown.includes(vercel.VERCEL_NEON_DRIZZLE_COMPARE_PATH)); + assert.doesNotMatch(markdown, /500 MB storage on Free/); + assert.doesNotMatch(markdown, /database branching/); + assert.doesNotMatch(markdown, /one plan|one bill|three bills|three accounts/); + assert.doesNotMatch(markdown, /\]\(here\)|\[here\]/); + assert.doesNotMatch(markdown, /zero cold starts|no cold starts/); + assert.doesNotMatch(markdown, /requires Prisma ORM/); + assert.doesNotMatch(markdown, /the only|the first|the best/); + assert.doesNotMatch(markdown, /listed in one place/); + assert.doesNotMatch(markdown, /When should I choose Kysely/); + assert.doesNotMatch(markdown, /Copy link|Copied/); + assert.doesNotMatch(markdown, /Prisma ORM is free to start|ORM is free to start/i); + const article = readFileSync(join(compareDir, "article.tsx"), "utf8"); + assert.match(article, /vercel: "\/compare\/vercel\.md"/); + const sections = readFileSync(join(compareDir, "vercel/_sections.tsx"), "utf8"); + assert.match(sections, /comparisonChooseTable/); + assert.match(sections, /max-w-4xl text-balance font-heading/); + assert.match(sections, /mt-6 max-w-3xl text-pretty/); + assert.match(sections, /comparisonProofCards/); + assert.match(sections, /comparisonDisclosure/); + assert.match(sections, /secondary: false/); + assert.match(sections, /instantDatabase: false/); + assert.match(sections, /primaryLabel: "Get started with Prisma"/); + assert.match(sections, /firstCheck: "Get started with Prisma and start building in minutes"/); + assert.doesNotMatch(sections, /create-db\.prisma\.io/); + assert.doesNotMatch(sections, /defaultValue=/); + assert.doesNotMatch(sections, /PageAiActions/); + assert.doesNotMatch(sections, /dark:/); + const neonSections = readFileSync(join(compareDir, "neon/_sections.tsx"), "utf8"); + assert.doesNotMatch(neonSections, /firstCheck:/); + assert.doesNotMatch(neonSections, /instantDatabase: false/); + const ai = readFileSync(join(compareDir, "page-ai-actions.tsx"), "utf8"); + assert.match(ai, /pageMdUrl.includes\("\/compare\/vercel\.md"\)/); + assert.match(ai, /I am choosing between Prisma Compute and Vercel for hosting a TypeScript app/); + assert.match(ai, /Marketplace database/); +}); + +test("vercel JSON facts carry a source and dateChecked on every value", async () => { + const facts = getVercelFacts(); + assert.equal(facts.path, "/compare/vercel"); + assert.equal(facts.dateChecked, vercel.VERCEL_UPDATED.iso); + assert.equal(facts.tools.vercel.softwareVersion, "current"); + assert.ok(!("license" in facts.tools.vercel)); + assert.equal(facts.features.length, vercel.COMPARE_ROWS.length); + for (const row of [...facts.features, ...facts.pricing]) { + assert.match(row.vercel.source, /^https?:\/\//); + assert.match(row.prisma.source, /^https?:\/\//); + assert.equal(row.vercel.dateChecked, facts.dateChecked); + assert.equal(row.prisma.dateChecked, facts.dateChecked); + } + const response = getVercelFactsResponse(); + assert.match(response.headers.get("content-type") ?? "", /^application\/json/); + assert.deepEqual(await response.json(), facts); + + const fields = getVercelArticleFields(); + const stats = getVercelReadingStats(); + assert.equal(fields.datePublished, vercel.VERCEL_PUBLISHED.iso); + assert.equal(fields.dateModified, vercel.VERCEL_UPDATED.iso); + assert.equal(WORDS_PER_MINUTE, 225); + assert.equal(fields.wordCount, stats.wordCount); + assert.equal(fields.timeRequired, `PT${stats.minutes}M`); + assert.equal(stats.minutes, readingMinutes(stats.wordCount)); + assert.ok(stats.wordCount > 800); + assert.deepEqual( + fields.about.map((entity) => entity.name), + ["Prisma", "Vercel"], + ); + assert.ok(!("license" in (fields.about[1] ?? {}))); +}); + +test("vercel metadata advertises Markdown and JSON alternates", () => { + const types = getComparisonMetadata("vercel").alternates?.types; + assert.ok(types); + assert.match(types["text/markdown"] ?? "", /\/compare\/vercel\.md$/); + assert.match(types["application/json"] ?? "", /\/compare\/vercel\.json$/); +}); diff --git a/apps/site/src/lib/markdown/comparisons.tsx b/apps/site/src/lib/markdown/comparisons.tsx index 1d727cc5f2..32a17e128f 100644 --- a/apps/site/src/lib/markdown/comparisons.tsx +++ b/apps/site/src/lib/markdown/comparisons.tsx @@ -22,6 +22,8 @@ import { import { getPlanetScalePageContent } from "@/app/compare/planetscale/_sections"; import { CHECKED as SUPABASE_CHECKED, SUPABASE_UPDATED } from "@/app/compare/supabase/_content"; import { getSupabasePageContent } from "@/app/compare/supabase/_sections"; +import { CHECKED as VERCEL_CHECKED, VERCEL_UPDATED } from "@/app/compare/vercel/_content"; +import { getVercelPageContent } from "@/app/compare/vercel/_sections"; import { CHECKED as VERCEL_NEON_DRIZZLE_CHECKED, VERCEL_NEON_DRIZZLE_UPDATED, @@ -38,6 +40,7 @@ export const comparisonContent = { neon: getNeonPageContent, planetscale: getPlanetScalePageContent, supabase: getSupabasePageContent, + vercel: getVercelPageContent, "vercel-neon-drizzle": getVercelNeonDrizzlePageContent, } satisfies Record ReactNode>; @@ -49,6 +52,7 @@ const comparisonFreshness = { neon: { checked: NEON_CHECKED, dateModified: NEON_UPDATED.iso }, planetscale: { checked: PLANETSCALE_CHECKED, dateModified: PLANETSCALE_UPDATED.iso }, supabase: { checked: SUPABASE_CHECKED, dateModified: SUPABASE_UPDATED.iso }, + vercel: { checked: VERCEL_CHECKED, dateModified: VERCEL_UPDATED.iso }, "vercel-neon-drizzle": { checked: VERCEL_NEON_DRIZZLE_CHECKED, dateModified: VERCEL_NEON_DRIZZLE_UPDATED.iso, diff --git a/apps/site/src/lib/markdown/markdown-parity.test.ts b/apps/site/src/lib/markdown/markdown-parity.test.ts index 71ab928363..4f333c0380 100644 --- a/apps/site/src/lib/markdown/markdown-parity.test.ts +++ b/apps/site/src/lib/markdown/markdown-parity.test.ts @@ -86,6 +86,7 @@ const PAGE_SOURCES: Record = { "/compare/neon": ["src/app/compare/neon/_sections.tsx"], "/compare/planetscale": ["src/app/compare/planetscale/_sections.tsx"], "/compare/supabase": ["src/app/compare/supabase/_sections.tsx"], + "/compare/vercel": ["src/app/compare/vercel/_sections.tsx"], "/compare/vercel-neon-drizzle": ["src/app/compare/vercel-neon-drizzle/_sections.tsx"], "/mcp": [ "src/app/mcp/page.tsx", @@ -124,6 +125,7 @@ const MIN_LITERAL_HEADINGS: Record = { "/compare/neon": 10, "/compare/planetscale": 10, "/compare/supabase": 10, + "/compare/vercel": 10, "/compare/vercel-neon-drizzle": 10, };