Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
411daba
site: lay out comparison pages with the docs template
cursoragent Oct 17, 2026
c78f2af
site: lay comparison articles out like the guides
cursoragent Oct 17, 2026
ba6e3fb
site: refine the Kysely comparison page
cursoragent Oct 17, 2026
cd4c2dc
site: design pass on the Kysely comparison
cursoragent Oct 17, 2026
49fb744
site: align the Kysely tables and keep feature labels in view
cursoragent Oct 17, 2026
93eb399
site: let Kysely code spans wrap and drop a repeated opener
cursoragent Oct 17, 2026
114da84
site: move the Kysely disclosure into a quiet note below the hero
cursoragent Oct 17, 2026
50eeb29
site: show the same query in Kysely and Prisma ORM on /compare/kysely
cursoragent Oct 17, 2026
c813ed6
site: add Martin Janse van Rensburg's byline and Article author to /c…
cursoragent Oct 17, 2026
de41e54
site: stack the Kysely byline beside the photo on phones
cursoragent Oct 17, 2026
a9a8b2e
site: build Eclipse before the site unit tests
cursoragent Oct 17, 2026
ed62ece
Highlight the active section in the comparison TOC
cursoragent Oct 17, 2026
72dad05
Kysely compare: yes/no table marks, heading copy links, round avatar,…
cursoragent Oct 17, 2026
f59a467
Compare article: right-rail TOC with short labels, mobile disclosure,…
cursoragent Oct 17, 2026
85d76a1
Kysely compare: database support matrix, same-job task table, Why Pri…
cursoragent Oct 17, 2026
2503602
Kysely compare: plain byline above the sources note in its own box
cursoragent Oct 18, 2026
8b3160e
Kysely compare: question headings and a reusable pricing table
cursoragent Oct 18, 2026
3d8ac0c
Kysely compare: Impeccable audit, ORM CTAs, stacked tables
cursoragent Oct 18, 2026
b4c3e52
Fix Kysely compare OG image types for the site build
cursoragent Oct 18, 2026
7d311ef
Stop Kysely compare tables from widening the phone layout
cursoragent Oct 18, 2026
f206f16
Compare: Prisma ORM close is free and open source, not a trial
cursoragent Oct 18, 2026
2c8e508
Compare: raise Kysely technical audit with small a11y and perf fixes
cursoragent Oct 18, 2026
4966dbb
Compare: omit forceMount so closed Kysely tabs unmount
cursoragent Oct 18, 2026
971e077
Compare: move Kysely Copy into the tab bar
cursoragent Oct 18, 2026
3888485
Compare: Kysely design pass for legend, copy, and open-in
cursoragent Oct 18, 2026
b333b2c
Compare: Open with AI logos and a supporting create-db line
cursoragent Oct 18, 2026
354cec9
Compare: keep Open-with-AI logos on one row at 390
cursoragent Oct 18, 2026
6f4d857
Compare: replace Copy page with an icon in the AI row
cursoragent Oct 18, 2026
daa35d7
Compare: move copy and AI marks under the TOC
cursoragent Oct 18, 2026
c1b9f59
Compare: lift Mongoose onto the Kysely article template
cursoragent Oct 18, 2026
ae97570
Compare: full-bleed hero, reading time, and confirmed author socials
cursoragent Oct 18, 2026
7223e0a
Compare: match the Codex guide hero card
cursoragent Oct 18, 2026
aac7b38
Compare: full-width disclosure and create-db hero CTA
cursoragent Oct 18, 2026
62f0dd9
Compare: point Create a database at create-db.prisma.io/web
cursoragent Oct 18, 2026
72a3ff5
Compare: rename create-db CTA to Create an instant database
cursoragent Oct 18, 2026
05e9c0e
Compare: match Codex measure, split quote cards, compress TOC
cursoragent Oct 18, 2026
5883447
Compare: restore TOC spacing and center the reading unit
cursoragent Oct 18, 2026
49737e2
Compare: match Codex hero width and replace Kysely FAQ
cursoragent Oct 18, 2026
b32b807
site: ignore fenced shell comments when counting Markdown headings
cursoragent Oct 18, 2026
1996810
Compare: put Convex on the shared Kysely/Mongoose chrome
cursoragent Oct 18, 2026
9bd3f3f
Compare: cite MongoDB sources in Mongoose body and open create-db in …
cursoragent Oct 19, 2026
5778c6b
Compare: update Mongoose heading assertion for the migration card
cursoragent Oct 19, 2026
70a5bb2
Compare: render the choose section as a stacked table
cursoragent Oct 19, 2026
3866ee2
Compare: drop the directory plug from the Convex hero dek
cursoragent Oct 19, 2026
ffc7fa7
Compare: add Prisma Postgres vs Neon on the shared chrome
cursoragent Oct 19, 2026
b60d4cc
Compare: add Prisma Postgres vs Supabase on the shared chrome
cursoragent Oct 19, 2026
e9291e8
site: advertise Neon and Supabase Markdown headers
cursoragent Oct 19, 2026
9247978
ci: ignore unreleased Neon and Supabase compare links
cursoragent Oct 19, 2026
3980426
Compare: add Prisma Postgres vs PlanetScale on the shared chrome
cursoragent Oct 19, 2026
6fe4ed1
docs: link the PlanetScale comparison on the marketing site
cursoragent Oct 19, 2026
f4c47be
Compare: retitle Prisma Postgres vs Supabase
cursoragent Oct 19, 2026
6049fee
Compare: resolve review feedback on the Postgres comparison pages
ankur-arch Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/lychee.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ jobs:
uses: lycheeverse/lychee-action@v2
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.
args: >-
--cache
--cache-exclude-status 429,500,502,503,504
Expand All @@ -56,6 +57,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)$'
--accept 200,201,204,301,302,303,307,308,403,429
${{ steps.changed-docs.outputs.all_changed_files }}
output: lychee/out.md
Expand Down Expand Up @@ -90,6 +92,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)$'
--accept 200,201,204,301,302,303,307,308,403,429
${{ steps.changed-docs.outputs.all_changed_files }}
output: lychee/out-retry.md
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/site-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,10 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile --filter "site..."

# Comparison pages import @prisma/eclipse, whose package entry is the
# built dist. The install does not produce it.
- name: Build Eclipse
run: pnpm --filter @prisma/eclipse build

- name: Run unit tests
run: pnpm --filter=site test
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ The [comparison table](#how-the-four-compare-with-neon) uses these ten criteria,
- What it does: [Prisma Postgres](https://www.prisma.io/docs/postgres) is managed Postgres, run by Prisma: PostgreSQL 17 with PgBouncer pooling included, reached by any Postgres client through a connection string. [Prisma Compute](https://www.prisma.io/docs/compute) runs TypeScript services (Node.js, Bun or Next.js) next to it in the same region, chosen from six, generally available since August 2026. With [Prisma Composer](https://www.prisma.io/docs/composer) you describe your whole app in TypeScript, each service and the database it depends on; `npx prisma dev module.ts` runs it on your laptop, and `npx prisma deploy module.ts` provisions the services on Prisma Compute and the database on Prisma Postgres.
- Pricing: Free $0, no credit card, no usage billing: 1M Compute requests and 200,000 database operations a month, 50 databases. Starter $10 base plus metered compute: 5M requests and 1M operations included, 10 GB of storage, daily backups kept 7 days and a spend limit; memory, active vCPU and bandwidth metered from the first unit, then $1 per million requests and $8 per million operations beyond the included amounts. [Prisma pricing](https://www.prisma.io/pricing), as of October 2026.
- Trade-offs: no point-in-time recovery, no preview that copies production data, one region per service. We make Prisma Compute and Prisma Postgres; the entries are in alphabetical order, and we checked Neon, Railway, Render, Supabase and Vercel against their own docs on 2026-09-30.
- Compare: [Prisma Postgres vs Neon pricing](/prisma-postgres-vs-neon-pricing-2026).
- Compare: [Prisma Postgres vs Neon](https://www.prisma.io/compare/neon) and [Prisma Postgres vs Neon pricing](/prisma-postgres-vs-neon-pricing-2026).

2. **Railway**
- Best for: deploying the repository you have, in any language, next to a Postgres template Railway maintains.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ If you want to complete both changes on the same day, deploy and verify the Pris

## Hosting the app too

If the application that uses this database still runs on another host, [Prisma Compute](/compute) can host it next to Prisma Postgres, in the same region, so the app and its database deploy together. Compute runs TypeScript apps (Node.js, Bun or Next.js) on Bun, in one region per service, and does not run WebSocket servers. An existing app gets a [Prisma Composer](/composer) declaration next to its server code, and Composer is in Early Access. For a Neon user, [Neon alternatives with app hosting and Postgres in one place](https://www.prisma.io/blog/neon-vs-prisma-postgres-app-hosting-in-one-place) compares this setup with Railway, Render and Supabase.
If the application that uses this database still runs on another host, [Prisma Compute](/compute) can host it next to Prisma Postgres, in the same region, so the app and its database deploy together. Compute runs TypeScript apps (Node.js, Bun or Next.js) on Bun, in one region per service, and does not run WebSocket servers. An existing app gets a [Prisma Composer](/composer) declaration next to its server code, and Composer is in Early Access. For a Neon user, [Prisma Postgres vs Neon](https://www.prisma.io/compare/neon) is the head-to-head, and [Neon alternatives with app hosting and Postgres in one place](https://www.prisma.io/blog/neon-vs-prisma-postgres-app-hosting-in-one-place) compares this setup with Railway, Render and Supabase.

## Other migration guides

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -329,5 +329,6 @@ If you want to complete both changes on the same day, deploy and verify the Pris

## Other migration guides

- [Prisma Postgres vs Supabase](https://www.prisma.io/compare/supabase)
- [Migrate from Neon to Prisma Postgres](/guides/switch-to-prisma-postgres/from-neon)
- [Migrate another PostgreSQL database to Prisma Postgres](/prisma-postgres/import-from-existing-database-postgresql)
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Run the same count against the local database. The [full import guide](/prisma-p

## Moving from Neon or Supabase?

The same dump and restore works from a hosted database. The [Neon guide](/guides/switch-to-prisma-postgres/from-neon) covers selecting the branch and the unpooled endpoint, and the [Supabase guide](/guides/switch-to-prisma-postgres/from-supabase) lists what does not move with the data: Auth users, Storage objects and Edge Functions.
The same dump and restore works from a hosted database. The [Neon guide](/guides/switch-to-prisma-postgres/from-neon) covers selecting the branch and the unpooled endpoint, and the [Supabase guide](/guides/switch-to-prisma-postgres/from-supabase) lists what does not move with the data: Auth users, Storage objects and Edge Functions. PlanetScale Postgres uses the same dump and restore over a direct (non-PgBouncer) connection; [Prisma Postgres vs PlanetScale](https://www.prisma.io/compare/planetscale) is the head-to-head.

The dump is a snapshot of the source at the moment `pg_dump` runs, so stop or drain writes to the source before the final dump and keep them stopped until you have verified the import and switched the application over, as the [full guide's export step](/prisma-postgres/import-from-existing-database-postgresql#3-stop-source-writes-and-export-the-final-snapshot) spells out. If you cannot pause writes for that long, this snapshot process is not the right fit: you need a separately tested process that captures and replays every write made after the snapshot.

Expand Down
3 changes: 3 additions & 0 deletions apps/site/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ const agentMarkdownPaths = [
"/compare/convex",
"/compare/kysely",
"/compare/mongoose",
"/compare/neon",
"/compare/planetscale",
"/compare/supabase",
];

/**
Expand Down
Binary file added apps/site/public/logos/customers/bucket.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions apps/site/src/app/compare/_content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,62 @@ export const COMPARISON_CTA = {
primaryCta: { label: "Get started free", href: "https://console.prisma.io" },
secondaryCta: { label: "Talk to us", href: "/contact" },
} as const;

/**
* Closing action on ORM comparisons (Kysely, Mongoose). Prisma ORM is free and
* open source, not a trial. The 500K+ line is how many people use it; it stays.
* The buttons go to the ORM quickstart and Prisma Postgres rather than a
* generic console pitch. Do not reuse COMPARISON_CTA.body here: that dek is
* for hosted Prisma Postgres (free plan, credit card).
*/
export const ORM_COMPARISON_CTA = {
headline: "Add Prisma ORM to an existing project",
headlineMaxWidth: "max-w-[28ch]",
body: "Prisma ORM is free and open source, and always will be.",
bodyMaxWidth: "max-w-[52ch]",
checks: [
{ label: "Add the library", color: "text-prism-cyan-500" },
{ label: "Read the ORM docs", color: "text-prism-yellow-400" },
{ label: "Trusted by 500K+ developers globally", color: "text-prism-red-500" },
],
primaryCta: {
label: "Get started with Prisma ORM",
href: "/docs/prisma-orm/quickstart/postgresql",
},
secondaryCta: {
label: "Get started with Prisma Postgres",
href: "https://console.prisma.io",
},
} as const;

/** Temporary Prisma Postgres in the browser, no account. */
export const CREATE_DB_WEB = "https://create-db.prisma.io/web";

/** Open create-db off the comparison so the reader does not lose the page. */
export const CREATE_DB_NEW_TAB = {
target: "_blank",
rel: "noopener noreferrer",
} as const;

export const ORM_HERO_CTAS = {
primary: {
label: "Get started with Prisma ORM",
href: "/docs/prisma-orm/quickstart/postgresql",
},
secondary: {
label: "Create an instant database",
href: CREATE_DB_WEB,
},
} as const;

/** Hero buttons on Postgres/platform comparisons (Convex). */
export const POSTGRES_HERO_CTAS = {
primary: {
label: "Get started with Prisma Postgres",
href: "https://console.prisma.io",
},
secondary: {
label: "Create an instant database",
href: CREATE_DB_WEB,
},
} as const;
100 changes: 100 additions & 0 deletions apps/site/src/app/compare/anchor-heading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
"use client";

import { useEffect, useRef, useState, type ReactNode } from "react";
import { Check, Link as LinkIcon } from "lucide-react";
import { COPIED_VISIBLE_MS } from "./copied";

function headingText(node: ReactNode): string {
if (typeof node === "string" || typeof node === "number") return String(node);
if (Array.isArray(node)) return node.map(headingText).join("");
if (node && typeof node === "object" && "props" in node) {
return headingText((node as { props: { children?: ReactNode } }).props.children);
}
return "";
}

/**
* A section heading with a copy-link button as a sibling, so the heading's
* accessible name stays the visible text. The button is absolutely positioned
* so it never moves the heading, and only appears on devices that can hover.
* Inside a frame or card, `placement="end"` puts it at the heading's right end
* instead, where the left gutter would fall outside the frame.
*/
export function AnchorHeading({
level,
id,
className,
placement = "gutter",
children,
}: {
level: 2 | 3;
id: string;
className?: string;
placement?: "gutter" | "end";
children: ReactNode;
}) {
const Tag = level === 2 ? "h2" : "h3";
const end = placement === "end";
const [copied, setCopied] = useState(false);
const [inTabOrder, setInTabOrder] = useState(false);
const timer = useRef<ReturnType<typeof setTimeout>>(undefined);
const label = headingText(children).trim();

useEffect(() => () => clearTimeout(timer.current), []);

async function copy() {
const url = new URL(window.location.href);
url.hash = id;
window.history.replaceState(window.history.state, "", url);
try {
await navigator.clipboard.writeText(url.toString());
} catch {
return;
}
setCopied(true);
clearTimeout(timer.current);
timer.current = setTimeout(() => setCopied(false), COPIED_VISIBLE_MS);
}

return (
<div
className="group/heading relative"
onMouseEnter={() => setInTabOrder(true)}
onMouseLeave={() => setInTabOrder(false)}
onFocusCapture={() => setInTabOrder(true)}
onBlurCapture={(event) => {
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {
setInTabOrder(false);
}
}}
>
<Tag id={id} tabIndex={-1} className={className}>
{children}
</Tag>
<span
className={`absolute top-0 hidden h-[1lh] items-center [@media(hover:hover)]:flex ${end ? "right-0" : "right-full pr-2"}`}
>
<button
type="button"
onClick={copy}
tabIndex={inTabOrder ? 0 : -1}
aria-label={label ? `Copy link to “${label}”` : "Copy link to this section"}
className="relative flex size-6 items-center justify-center rounded-md text-foreground/40 opacity-0 transition-[opacity,color] duration-150 group-hover/heading:opacity-100 group-focus-within/heading:opacity-100 hover:text-foreground/70 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-foreground/70 motion-reduce:transition-none after:absolute after:-inset-2.5 after:content-['']"
>
{copied ? (
<Check className="size-3.5" strokeWidth={2} aria-hidden />
) : (
<LinkIcon className="size-3.5" strokeWidth={2} aria-hidden />
)}
<span
aria-hidden
className={`pointer-events-none absolute whitespace-nowrap ${end ? "top-1/2 right-full mr-1 -translate-y-1/2" : "bottom-full left-1/2 mb-1 -translate-x-1/2"} rounded bg-foreground px-1.5 py-0.5 font-sans text-[11px] leading-4 font-normal text-background transition-opacity duration-200 after:content-['Copied'] motion-reduce:transition-none ${copied ? "opacity-90" : "opacity-0"}`}
/>
<span role="status" className="sr-only">
{copied ? "Link copied" : ""}
</span>
</button>
</span>
</div>
);
}
Loading
Loading