From cd629005796f471b92cc4762681d53ca64230bc1 Mon Sep 17 00:00:00 2001 From: Carla Goncalves Date: Wed, 25 Mar 2026 19:00:29 +0000 Subject: [PATCH 1/8] Add first step of client page --- apps/site/src/app/client/page.tsx | 350 ++++++++++++++++++++ apps/site/src/components/client/api-data.ts | 194 +++++++++++ apps/site/src/components/client/api.tsx | 266 +++++++++++++++ 3 files changed, 810 insertions(+) create mode 100644 apps/site/src/app/client/page.tsx create mode 100644 apps/site/src/components/client/api-data.ts create mode 100644 apps/site/src/components/client/api.tsx diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx new file mode 100644 index 0000000000..e34886201c --- /dev/null +++ b/apps/site/src/app/client/page.tsx @@ -0,0 +1,350 @@ +import type { Metadata } from "next"; +import { + SITE_HOME_DESCRIPTION, + SITE_HOME_TITLE, +} from "../../lib/blog-metadata"; +import { Action, Button, Card } from "@prisma/eclipse"; +import API from "@/components/client/api"; + +export const metadata: Metadata = { + title: SITE_HOME_TITLE, + description: SITE_HOME_DESCRIPTION, +}; + +const migrateSteps: HeroCodeStep[] = [ + { + title: "Creating a new model", + migrateFileName: "init/20210211160000_init/migration.sql", + schema: `model User { + id Int @id @default(autoincrement()) + email String @unique + name String? +}`.trim(), + migrateFileContents: `-- CreateTable +CREATE TABLE "User" ( + "id" SERIAL NOT NULL, + "email" TEXT NOT NULL, + "name" TEXT, + + PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User.email_unique" IN "User"("email");`.trim(), + arrowOffset: { + x: -60, + y: 0, + rotation: 0, + }, + }, + { + title: "Adding a model", + migrateFileName: "init/20210325160100_add_post/migration.sql", + schema: ` +model User { + id Int @id @default(autoincrement()) + email String @unique + name String? ++ posts Post[] +} ++ ++model Post { ++ id Int @id @default(autoincrement()) ++ created DateTime @default(now()) ++ title String @db.VarChar(70) ++ content String ++ author User? @relation(fields: [authorId], references: [id]) ++ authorId Int ++}`.trim(), + migrateFileContents: ` +-- CreateTable +CREATE TABLE "Post" ( + "id" SERIAL NOT NULL, + "created" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "title" VARCHAR(70) NOT NULL, + "content" TEXT NOT NULL, + "authorId" INTEGER NOT NULL, + PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Post" ADD FOREIGN KEY ("authorId") REFERENCES +"User"("id") ON DELETE CASCADE ON UPDATE CASCADE;`.trim(), + arrowOffset: { + x: 190, + y: -20, + rotation: -70, + }, + }, +]; + +export default function Client() { + return ( +
+
+
+
+
+ Prisma Client +
+

+ Intuitive database client for TypeScript and Node.js +
+ Database Migrations +

+
+

+ The Prisma Client works seamlessly across languages and databases. + Ship faster by writing less SQL. Avoid mistakes with a fully type-safe + API tailored specifically for your app. +

+
+
+
+ +
+
+
+
+
+ +
+ + + +

+ Auto-generated +

+
+

+ Migrations are automatically generated so you don't have to + write the SQL by hand. +

+
+ +
+ + + +

+ Deterministic/ Repeatable +

+
+

+ Migrate generates SQL migrations, ensuring migrations will + always result in the same database schema across environments. +

+
+ +
+ + + +

+ Customizable +

+
+

+ Generated SQL migrations can be fully customized giving you full + control over the exact changes. +

+
+
+
+
+
+
+
+
+
+ Iteration +
+

+ Fast in development +

+
    +
  • + +
    +

    + Prototype fast without migrations +

    +

    + While prototyping you can create the database schema + quickly using the prisma db push command without creating + migrations. +

    +
    +
  • + +
  • + +
    +

    + Integrated seeding +

    +

    + Quickly seed your database with data by defining a seed + script in JavaScript, TypeScript or Shell. +

    +
    +
  • +
  • + +
    +

    + Smart problem resolution +

    +

    + Migrate detects database schema drift and assists you in + resolving them. +

    +
    +
  • +
+
+
+
+ Deployment +
+

+ Reliable in Production +

+
    +
  • + +
    +

    + Dedicated production workflows +

    +

    + Migrate supports dedicated workflows for carrying out + migrations safely in production. +

    +
    +
  • +
  • + +
    +

    + CI/CD Integration +

    +

    + Migrate can be integrated into CI/CD pipelines, e.g. + GitHub Actions, to automate applying migrations before + deployment. +

    +
    +
  • +
  • + +
    +

    + Conflict detection and resolution +

    +

    + Migrate keeps track of applied migrations and provides + tools to detect and resolve conflicts and drifts between + migrations and the database schema. +

    +
    +
  • +
+
+
+
+
+
+
+
+ {/* Card 1 */} + +
+ + + +

+ Seamless integration with Prisma Client +

+
+

+ When using Prisma Migrate with Prisma Client, schema changes are + type checked in your application code. This eliminates errors + that arise when database schema changes require changes to the + application code. +

+
+ + {/* Card 2 */} + +
+ + + +

+ Declarative data modelling +

+
+

+ Prisma Migrate generates migrations based on changes in the + Prisma schema – a human-readable declarative definition of your + database schema. This allows you to focus on your desired + database schema rather than the steps to get there. +

+
+ + {/* Card 3 */} + +
+ + + +

+ Version control for your database +

+
+

+ With Prisma Migrate, generated migrations are tracked in your + Git repository, allowing you to make changes to your database + schema in tandem with your application code. +

+
+ + {/* Card 4 */} + +
+ + + +

+ Streamlined collaboration +

+
+

+ With Prisma Migrate, generated migrations are tracked in your + Git repository, allowing you to make changes to your database + schema in tandem with your application code. +

+
+ + {/* Card 5 */} + +
+ + + +

+ Bring your own project +

+
+

+ Prisma Migrate can be adopted in any existing project that uses + PostgreSQL, MySQL, MariaDB, SQL Server, CockroachDB or SQLite. +

+
+
+
+
+
+ ); +} diff --git a/apps/site/src/components/client/api-data.ts b/apps/site/src/components/client/api-data.ts new file mode 100644 index 0000000000..867181d6ac --- /dev/null +++ b/apps/site/src/components/client/api-data.ts @@ -0,0 +1,194 @@ +// DO NOT EDIT BY HAND +// Generated by `scripts/generate-explore.js` at 2022-11-07T16:31:03.482Z. +// You can adjust the examples in ui/explore, then run `node scripts/generate-explore.js`. + +export const apiItems = [ + { + label: "Reading Data", + value: "1-reading-data", + functions: [ + { + name: "Find Records", + jsCodeBlocks: [ + "// Find all posts\nconst allPosts = await prisma.post.findMany()", + "// Find a user by ID\nconst userById = await prisma.user.findUnique({\n where: {\n id: 2\n }\n})", + "// Find a user by email\nconst userByEmail = await prisma.user.findUnique({\n where: {\n email: 'ada@prisma.io'\n },\n})", + "// Find the first user that starts with Ada\nconst userByName = await prisma.user.findFirst({\n where: {\n name: {\n startsWith: 'Ada'\n }\n },\n})", + "// Select specific fields\nconst userName = await prisma.user.findUnique({\n where: {\n email: 'ada@prisma.io',\n },\n select: {\n name: true,\n email: true,\n },\n})", + ], + tsCodeBlocks: [ + "// Find all posts\nconst allPosts: Post[] = await prisma.post.findMany()", + "// Find a user by ID\nconst userById: User | null = await prisma.user.findUnique({\n where: {\n id: 2,\n },\n})", + "// Find a user by email\nconst userByEmail = await prisma.user.findUnique({\n where: {\n email: 'ada@prisma.io',\n },\n})", + "// Find the first user that contains Ada\nconst userByName = await prisma.user.findFirst({\n where: {\n name: {\n contains: 'Ada',\n },\n },\n})", + "// Select specific fields\nconst userName = await prisma.user.findUnique({\n where: {\n email: 'ada@prisma.io',\n },\n select: {\n name: true,\n email: true,\n },\n})", + ], + }, + { + name: "Traverse Relations", + jsCodeBlocks: [ + "// Retrieve the posts of a user\nconst postsByUser = await prisma.user\n .findUnique({ where: { email: 'ada@prisma.io' } })\n .posts()", + "// Retrieve the categories of a post\nconst categoriesOfPost = await prisma.post\n .findUnique({ where: { id: 1 } })\n .categories()", + "// Retrieve the profile of a user via a specific post\nconst authorProfile = await prisma.post\n .findUnique({ where: { id: 1 } })\n .author()\n .profile()", + "// Return all users and include their posts and profile\nconst users = await prisma.user.findMany({\n include: {\n posts: true,\n profile: true,\n },\n})", + "// Select all users and all their post titles\nconst userPosts = await prisma.user.findMany({\n select: {\n name: true,\n posts: {\n select: {\n title: true,\n },\n },\n },\n})", + ], + tsCodeBlocks: [ + "// Retrieve the posts of a user\nconst postsByUser: Post[] = await prisma.user\n .findUnique({ where: { email: 'ada@prisma.io' } })\n .posts()", + "// Retrieve the categories of a post\nconst categoriesOfPost: Category[] = await prisma.post\n .findUnique({ where: { id: 1 } })\n .categories()", + "// Retrieve the profile of a user via a specific post\nconst authorProfile: Profile | null = await prisma.post\n .findUnique({ where: { id: 1 } })\n .author()\n .profile()", + "// Return all users and include their posts and profile\nconst users: User[] = await prisma.user.findMany({\n include: {\n posts: true,\n profile: true,\n },\n})", + "// Select all users and all their post titles\nconst userPosts = await prisma.user.findMany({\n select: {\n name: true,\n posts: {\n select: {\n title: true,\n },\n },\n },\n})", + ], + }, + { + name: "Order By, Limits & Cursors", + jsCodeBlocks: [ + "// Sort posts alphabetically\nconst alphabeticPosts = await prisma.post.findMany({\n orderBy: { title: 'asc' },\n})", + "// Order by most prolific authors\nconst prolificAuthors = await prisma.user.findMany({\n orderBy: {\n posts: {\n _count: 'asc',\n },\n },\n})", + "// Find the second page of posts\nconst secondPagePosts = await prisma.post.findMany({\n take: 5,\n skip: 5,\n})", + "// Find the last 5 posts\nconst lastPosts = await prisma.post.findMany({\n take: -5,\n})", + "// Find the next 5 posts after post id 2\nconst paginatedPosts3 = await prisma.post.findMany({\n take: 5,\n cursor: {\n id: 2,\n },\n})", + "// Find the last 5 posts before post id 2\nconst paginatedPosts4 = await prisma.post.findMany({\n take: -5,\n cursor: {\n id: 11,\n },\n})", + ], + tsCodeBlocks: [ + "// Sort posts alphabetically\nconst alphabeticPosts: Post[] = await prisma.post.findMany({\n orderBy: { title: 'asc' },\n})", + "// Order by most prolific authors\nconst prolificAuthors: Category[] = await prisma.user.findMany({\n orderBy: {\n posts: {\n _count: 'asc',\n },\n },\n})", + "// Find the second page of posts\nconst secondPagePosts: Post[] = await prisma.post.findMany({\n take: 5,\n skip: 5,\n})", + "// Find the last 5 posts\nconst lastPosts: Post[] = await prisma.post.findMany({\n take: -5,\n})", + "// Find the next 5 posts after post id 2\nconst paginatedPosts3: Post[] = await prisma.post.findMany({\n take: 5,\n cursor: {\n id: 2,\n },\n})", + "// Find the last 5 posts before post id 2\nconst paginatedPosts4: Post[] = await prisma.post.findMany({\n take: -5,\n cursor: {\n id: 11,\n },\n})", + ], + }, + { + name: "Aggregates & Group By", + jsCodeBlocks: [ + "// Count the number of users\nconst count = await prisma.user.count()", + "// Average age of our users\nconst averageAge = await prisma.user.aggregate({\n _avg: {\n age: true,\n },\n})", + "// Group users by country\nconst groupUsers = await prisma.user.groupBy({\n by: ['country'],\n _count: {\n id: true,\n },\n})", + "// Group users by country with more than 3 users.\nconst usersByCountry = await prisma.user.groupBy({\n by: ['country'],\n _count: {\n country: true,\n },\n having: {\n country: {\n _count: {\n gt: 3,\n },\n },\n },\n})", + ], + tsCodeBlocks: [ + "// Count the number of users\nconst count = await prisma.user.count()", + "// Average age of our users\nconst averageAge = await prisma.user.aggregate({\n _avg: {\n age: true,\n },\n})", + "// Group users by country\nconst groupUsers = await prisma.user.groupBy({\n by: ['country'],\n _count: {\n id: true,\n },\n})", + "// Group users by country with more than 3 users.\nconst usersByCountry = await prisma.user.groupBy({\n by: ['country'],\n _count: {\n country: true,\n },\n having: {\n country: {\n _count: {\n gt: 3,\n },\n },\n },\n})", + ], + }, + ], + }, + { + label: "Writing Data", + value: "2-writing-data", + functions: [ + { + name: "Create Records", + jsCodeBlocks: [ + "// Create a user\nconst user = await prisma.user.create({\n data: {\n email: 'elsa@prisma.io',\n name: 'Elsa Prisma',\n country: 'Italy',\n age: 30,\n },\n})", + "// Create a user and two posts\nconst userWithPosts = await prisma.user.create({\n data: {\n email: 'elsa@prisma.io',\n name: 'Elsa Prisma',\n country: 'Italy',\n age: 30,\n posts: {\n create: [\n {\n title: 'Include this post!',\n },\n {\n title: 'Include this post!',\n },\n ],\n },\n },\n})", + "// Create a customer and connect to a user\nconst profile = await prisma.profile.create({\n data: {\n bio: 'Flying Trapeze Artist. Prisma developer.',\n user: {\n connect: {\n id: 10,\n },\n },\n },\n})", + "// Create many users at once\nconst users = await prisma.user.createMany({\n data: [\n {\n name: 'Bob',\n email: 'bob@prisma.io',\n country: 'USA',\n age: 24,\n },\n {\n name: 'Yewande',\n email: 'yewande@prisma.io',\n country: 'Zimbabwe',\n age: 19,\n },\n {\n name: 'Angelique',\n email: 'angelique@prisma.io',\n country: 'Greece',\n age: 43,\n },\n ],\n})", + "// Create many users at once while skipping duplicates\nconst moreUsers = await prisma.user.createMany({\n data: [\n {\n name: 'Bob',\n email: 'bob@prisma.io',\n country: 'USA',\n age: 24,\n },\n {\n name: 'Bobby',\n email: 'bob@prisma.io',\n country: 'United States of America',\n age: 24,\n }, // Duplicate unique key is ignored!\n {\n name: 'Yewande',\n email: 'yewande@prisma.io',\n country: 'Zimbabwe',\n age: 19,\n },\n {\n name: 'Angelique',\n email: 'angelique@prisma.io',\n country: 'Greece',\n age: 43,\n },\n ],\n skipDuplicates: true, // Skip 'Bobby'\n})", + ], + tsCodeBlocks: [ + "// Create a user\nconst user: User = await prisma.user.create({\n data: {\n email: 'elsa@prisma.io',\n name: 'Elsa Prisma',\n country: 'Italy',\n age: 30,\n },\n})", + "// Create a user and two posts\nconst userWithPosts: User = await prisma.user.create({\n data: {\n email: 'elsa@prisma.io',\n name: 'Elsa Prisma',\n country: 'Italy',\n age: 30,\n posts: {\n create: [\n {\n title: 'Include this post!',\n },\n {\n title: 'Include this post!',\n },\n ],\n },\n },\n})", + "// Create a customer and connect to a user\nconst profile: Profile = await prisma.profile.create({\n data: {\n bio: 'Flying Trapeze Artist. Prisma developer.',\n user: {\n connect: {\n id: 10,\n },\n },\n },\n})", + "// Create many users at once\nconst users: Prisma.BatchPayload = await prisma.user.createMany({\n data: [\n {\n name: 'Bob',\n email: 'bob@prisma.io',\n country: 'USA',\n age: 24,\n },\n {\n name: 'Yewande',\n email: 'yewande@prisma.io',\n country: 'Zimbabwe',\n age: 19,\n },\n {\n name: 'Angelique',\n email: 'angelique@prisma.io',\n country: 'Greece',\n age: 43,\n },\n ],\n})", + "// Create many users at once while skipping duplicates\nconst moreUsers: Prisma.BatchPayload = await prisma.user.createMany({\n data: [\n {\n name: 'Bob',\n email: 'bob@prisma.io',\n country: 'USA',\n age: 24,\n },\n {\n name: 'Bobby',\n email: 'bob@prisma.io',\n country: 'United States of America',\n age: 24,\n }, // Duplicate unique key is ignored!\n {\n name: 'Yewande',\n email: 'yewande@prisma.io',\n country: 'Zimbabwe',\n age: 19,\n },\n {\n name: 'Angelique',\n email: 'angelique@prisma.io',\n country: 'Greece',\n age: 43,\n },\n ],\n skipDuplicates: true, // Skip 'Bobby'\n})", + ], + }, + { + name: "Update Records", + jsCodeBlocks: [ + "// Update an existing user\nconst alice = await prisma.user.update({\n data: {\n role: 'ADMIN',\n },\n where: {\n email: 'alice@prisma.io',\n },\n})", + "// Change the author of a post in a single transaction\nconst updatedPost = await prisma.post.update({\n where: {\n id: 2,\n },\n data: {\n author: {\n connect: {\n email: 'alice@prisma.io',\n },\n },\n },\n})", + "// Connect a post to a user, creating the post if it isn't found\nconst updatedUser = await prisma.user.update({\n where: {\n id: 1,\n },\n data: {\n posts: {\n connectOrCreate: {\n create: {\n title: 'The Gray Gatsby',\n },\n where: {\n id: 10,\n },\n },\n },\n },\n})", + "// Update all users with the country Deutschland\nconst updatedUsers = await prisma.user.updateMany({\n data: {\n country: 'Germany',\n },\n where: {\n country: 'Deutschland',\n },\n})", + ], + tsCodeBlocks: [ + "// Update an existing user\nconst alice: User = await prisma.user.update({\n data: {\n role: 'ADMIN',\n },\n where: {\n email: 'alice@prisma.io',\n },\n})", + "// Change the author of a post in a single transaction\nconst updatedPost: Post = await prisma.post.update({\n where: {\n id: 2,\n },\n data: {\n author: {\n connect: {\n email: 'alice@prisma.io',\n },\n },\n },\n})", + "// Connect a post to a user, creating the post if it isn't found\nconst updatedUser: User = await prisma.user.update({\n where: {\n id: 1,\n },\n data: {\n posts: {\n connectOrCreate: {\n create: {\n title: 'The Gray Gatsby',\n },\n where: {\n id: 10,\n },\n },\n },\n },\n})", + "// Update all users with the country Deutschland\nconst updatedUsers: Prisma.BatchPayload = await prisma.user.updateMany({\n data: {\n country: 'Germany',\n },\n where: {\n country: 'Deutschland',\n },\n})", + ], + }, + { + name: "Delete Records", + jsCodeBlocks: [ + "// Delete an existing user\nconst deletedUser = await prisma.user.delete({\n where: {\n email: 'alice@prisma.io',\n },\n})", + "// Delete all the admins at once\nconst deletedAdmins = await prisma.user.deleteMany({\n where: {\n role: 'ADMIN',\n },\n})", + ], + tsCodeBlocks: [ + "// Delete an existing user\nconst deletedUser: User = await prisma.user.delete({\n where: {\n email: 'alice@prisma.io',\n },\n})", + "// Delete all the admins at once\nconst deletedAdmins: Prisma.BatchPayload = await prisma.user.deleteMany({\n where: {\n role: 'ADMIN',\n },\n})", + ], + }, + { + name: "Upsert Records", + jsCodeBlocks: [ + "// Create Alice or update her role to admin.\nconst alice = await prisma.user.upsert({\n update: {\n role: 'ADMIN',\n },\n where: {\n email: 'alice@prisma.io',\n },\n create: {\n name: 'Alice',\n email: 'alice@prisma.io',\n country: 'England',\n role: 'ADMIN',\n age: 43,\n },\n})", + ], + tsCodeBlocks: [ + "// Create Alice or update her role to admin.\nconst alice: User = await prisma.user.upsert({\n update: {\n role: 'ADMIN',\n },\n where: {\n email: 'alice@prisma.io',\n },\n create: {\n name: 'Alice',\n email: 'alice@prisma.io',\n country: 'England',\n role: 'ADMIN',\n age: 43,\n },\n})", + ], + }, + ], + }, + { + label: "Advanced Patterns", + value: "3-advanced-patterns", + functions: [ + { + name: "Transactions", + jsCodeBlocks: [ + '// Create Bob and update Carol as a batch within a transaction\nawait prisma.$transaction([\n prisma.user.create({\n data: {\n name: "Bob",\n email: "bob@prisma.io",\n age: 49,\n country: "USA",\n },\n }),\n prisma.user.update({\n where: {\n email: "carol@prisma.io"\n },\n data: {\n country: "Germany",\n },\n }),\n])', + " // Example function turning an email into a country\n const locate = async (email) => ({ country: 'Germany' })\n const geo = await locate(bob.email)\n return await tx.user.update({\n where: {\n id: bob.id,\n },\n data: {\n country: geo.country,\n },\n })\n})", + ], + tsCodeBlocks: [ + "// Create Bob and update Carol as a batch within a transaction\nawait prisma.$transaction([\n prisma.user.create({\n data: {\n name: 'Bob',\n email: 'bob@prisma.io',\n age: 49,\n country: 'USA',\n },\n }),\n prisma.user.update({\n where: {\n email: 'carol@prisma.io',\n },\n data: {\n country: 'Germany',\n },\n }),\n])", + " // Example function turning an email into a country\n const locate = async (email: string) => ({ country: 'Germany' })\n const geo = await locate(bob.email)\n return await tx.user.update({\n where: {\n id: bob.id,\n },\n data: {\n country: geo.country,\n },\n })\n})", + ], + }, + { + name: "Middleware", + jsCodeBlocks: [ + "// Timing middleware\nprisma.$use(async (params, next) => {\n const before = Date.now()\n const result = await next(params)\n const after = Date.now()\n console.log(\n `Query ${params.model}.${params.action} took ${after - before}ms`,\n )\n return result\n})", + "// Hash a User password automatically\nprisma.$use(async (params, next) => {\n if (params.model === 'User') {\n if (params.action === 'create' || params.action === 'update') {\n if (params.args.data.password) {\n const hashPass = hash(params.args.data.password)\n params.args.data.password = hashPass\n }\n }\n }\n return next(params)\n})", + ], + tsCodeBlocks: [ + "// Timing middleware\nprisma.$use(async (params, next) => {\n const before = Date.now()\n const result = await next(params)\n const after = Date.now()\n console.log(\n `Query ${params.model}.${params.action} took ${after - before}ms`,\n )\n return result\n})", + "// Hash a User password automatically\nprisma.$use(async (params, next) => {\n if (params.model === 'User') {\n if (params.action === 'create' || params.action === 'update') {\n if (params.args.data.password) {\n const hashPass = hash(params.args.data.password)\n params.args.data.password = hashPass\n }\n }\n }\n return next(params)\n})", + ], + }, + { + name: "Raw Queries with TypedSQL", + tsCodeBlocks: [ + '-- in selectUserByEmail.sql\nSELECT * FROM "User" WHERE email = ${email};\n\n // in app.ts\nconst users: User[] = await prisma.$queryRawTyped(selectUserByEmail(email))', + '-- in select5PostsRandomly.sql\nSELECT * FROM "Post" order by random() limit 5\n\n // in app.ts\nconst posts: Post[] = await prisma.$queryRawTyped(select5PostsRandomly())', + '-- in clearUsersTable.sql\nDELETE from "User"\n\n // in app.ts\nconst clearUsersTable = await prisma.$executeRawTyped(clearUsersTable())', + '-- in dropUsersTable.sql\nTRUNCATE TABLE "User" CASCADE\n\n // in app.ts\nconst dropUsersTable = await prisma.$executeRawTyped(dropUsersTable())', + ], + }, + { + name: "Logging", + tsCodeBlocks: [ + "// Enable query logging\nconst prisma = new PrismaClient({\n log: [\n {\n emit: 'event',\n level: 'query',\n },\n ],\n})\nprisma.$on('query', (e) => {\n console.log('Query: ' + e.query)\n console.log('Params: ' + e.params)\n console.log('Duration: ' + e.duration + 'ms')\n})", + ], + }, + ], + }, + { + label: "Prisma Schema", + value: "4-prisma-schema", + functions: [ + { + name: "Data Model", + prismaCodeBlock: + '// This is your Prisma schema file. Learn more in the\n// documentation: https://pris.ly/d/prisma-schema\n\ndatasource db {\n provider = "postgres"\n url = env("DATABASE_URL")\n}\n\ngenerator client {\n provider = "prisma-client-js"\n previewFeatures = ["interactiveTransactions"]\n}\n\nmodel User {\n id Int @id @default(autoincrement())\n createdAt DateTime @default(now())\n email String @unique\n name String\n age Int\n role Role @default(USER)\n country String\n posts Post[]\n profile Profile?\n}\n\nmodel Profile {\n id Int @id @default(autoincrement())\n bio String\n user User @relation(fields: [userId], references: [id])\n userId Int @unique\n}\n\nmodel Post {\n id Int @id @default(autoincrement())\n createdAt DateTime @default(now())\n title String\n published Boolean @default(false)\n categories Category[] @relation(references: [id])\n author User @relation(fields: [authorId], references: [id])\n authorId Int\n}\n\nmodel Category {\n id Int @id @default(autoincrement())\n name String\n posts Post[] @relation(references: [id])\n}\n\nenum Role {\n USER\n ADMIN\n}\n', + jsCodeBlocks: [""], + tsCodeBlocks: [""], + }, + ], + }, +]; diff --git a/apps/site/src/components/client/api.tsx b/apps/site/src/components/client/api.tsx new file mode 100644 index 0000000000..4cb8e28a02 --- /dev/null +++ b/apps/site/src/components/client/api.tsx @@ -0,0 +1,266 @@ +"use client"; +import React, { useEffect, useState } from "react"; +import { cn } from "@/lib/cn"; + +import { apiItems } from "./api-data"; +import { + CodeBlock, + Button, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@prisma/eclipse"; +import { prisma_highlighter as getHighlighter } from "@/lib/shiki_prisma"; + +const langOptions = [ + { + value: "ts", + label: "TypeScript", + }, + { + value: "js", + label: "JavaScript", + }, +]; + +const CurvedArrow = (props: any) => ( + + + +); + +const icons: any = { + js: , + ts: , +}; + +const renderItem = (item: any) => { + return ( +
+ {icons[item.value]}   + {item.label} +
+ ); +}; + +const API = () => { + const [funcSelected, setFuncSelected]: any = useState( + apiItems[0].functions[0], + ); + + const [selectedAPIItem, setSelectedAPIItem] = useState(apiItems[0]); + + const [selectedLang, setSelectedLang] = useState(langOptions[0]); + const [highlightedCode, setHighlightedCode] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + const handleChange = (item: any) => { + setSelectedLang(item); + }; + + const blockType = + selectedLang.value === "js" ? "jsCodeBlocks" : "tsCodeBlocks"; + + const codeBlockSelected = funcSelected[blockType]; + + // Load highlighted code when funcSelected or selectedLang changes + useEffect(() => { + let isMounted = true; + + const loadHighlightedCode = async () => { + setIsLoading(true); + try { + const highlighter = await getHighlighter(); + const codeBlocks: string[] = []; + + if ("prismaCodeBlock" in funcSelected) { + const html = await highlighter.codeToHtml( + funcSelected["prismaCodeBlock"], + { + lang: "prisma", + theme: "prisma-dark", + }, + ); + codeBlocks.push(html); + } + + if (codeBlockSelected && !("prismaCodeBlock" in funcSelected)) { + for (let index = 0; index < codeBlockSelected.length; index++) { + const html = await highlighter.codeToHtml( + codeBlockSelected[index], + { + lang: "typescript", + theme: "prisma-dark", + }, + ); + codeBlocks.push(html); + } + } + + if (isMounted) { + setHighlightedCode(codeBlocks); + setIsLoading(false); + } + } catch (error) { + console.error("Failed to highlight code:", error); + if (isMounted) { + setIsLoading(false); + } + } + }; + + loadHighlightedCode(); + + return () => { + isMounted = false; + }; + }, [funcSelected, selectedLang, blockType, codeBlockSelected]); + + const contentBlocks = highlightedCode.map((html, index) => ( + .absolute]:text-foreground-neutral-reverse-weak [&>.absolute]:hover:text-foreground-neutral px-4", + )} + > + {isLoading ? ( +
Loading...
+ ) : ( +
+ )} + + )); + + const CodeUIItems = ({ item, blockType }: any) => { + const labelToDisplay = item.functions.filter( + (i: any) => i[blockType] && i[blockType].length > 0, + ); + return ( +
    + {labelToDisplay.map((func: any, fIndex: number) => { + const setNewBlocks = () => setFuncSelected(func); + return ( +
  • +
    setNewBlocks()} + > + {func.name} +
    +
  • + ); + })} +
+ ); + }; + + const handleCodeBlockChange = (item: any) => { + setSelectedAPIItem(item); + setFuncSelected(item.functions[0]); + }; + + return ( +
+
+
+ +
+ +
+ Prisma Client +
+

+ Explore the
Prisma Client API +

+

+ From simple reads to complex nested writes, the Prisma Client supports + a wide range of operations to help you make the most of your data. +

+
+
+ +
+ +
+
+ +
+ {} +
+
+ {contentBlocks} +
+
+ ); +}; + +export default API; From 30594c9b5ba11966064000533c9b0d58dd907a6a Mon Sep 17 00:00:00 2001 From: Carla Goncalves Date: Wed, 25 Mar 2026 19:36:43 +0000 Subject: [PATCH 2/8] Update first section of client --- apps/site/src/app/client/page.tsx | 2 +- apps/site/src/components/client/api.tsx | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx index e34886201c..16b74e5dfa 100644 --- a/apps/site/src/app/client/page.tsx +++ b/apps/site/src/app/client/page.tsx @@ -84,7 +84,7 @@ export default function Client() {
-
+
Prisma Client

diff --git a/apps/site/src/components/client/api.tsx b/apps/site/src/components/client/api.tsx index 4cb8e28a02..a9ea9913fa 100644 --- a/apps/site/src/components/client/api.tsx +++ b/apps/site/src/components/client/api.tsx @@ -36,7 +36,7 @@ const CurvedArrow = (props: any) => ( > @@ -183,10 +183,6 @@ const API = () => {
- -
- Prisma Client -

Explore the
Prisma Client API

From 063019f09649327dae9729e9af392ec789cbcac4 Mon Sep 17 00:00:00 2001 From: Carla Goncalves Date: Wed, 25 Mar 2026 21:44:51 +0000 Subject: [PATCH 3/8] Add client page --- .../public/icons/technologies/mariadb.svg | 47 ++ .../icons/technologies/mongodbsimple.svg | 8 + .../public/icons/technologies/mysqlsimple.svg | 44 ++ .../public/illustrations/client/client_0.svg | 101 ++++ .../illustrations/client/client_0_light.svg | 101 ++++ .../public/illustrations/client/client_1.svg | 65 +++ .../illustrations/client/client_1_light.svg | 38 ++ apps/site/src/app/client/page.tsx | 535 ++++++++---------- apps/site/src/components/client/api.tsx | 4 +- .../site/src/components/client/technology.tsx | 36 ++ .../homepage/card-section/card-section.tsx | 6 +- 11 files changed, 693 insertions(+), 292 deletions(-) create mode 100644 apps/site/public/icons/technologies/mariadb.svg create mode 100644 apps/site/public/icons/technologies/mongodbsimple.svg create mode 100644 apps/site/public/icons/technologies/mysqlsimple.svg create mode 100644 apps/site/public/illustrations/client/client_0.svg create mode 100644 apps/site/public/illustrations/client/client_0_light.svg create mode 100644 apps/site/public/illustrations/client/client_1.svg create mode 100644 apps/site/public/illustrations/client/client_1_light.svg create mode 100644 apps/site/src/components/client/technology.tsx diff --git a/apps/site/public/icons/technologies/mariadb.svg b/apps/site/public/icons/technologies/mariadb.svg new file mode 100644 index 0000000000..ef34e995a6 --- /dev/null +++ b/apps/site/public/icons/technologies/mariadb.svg @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/apps/site/public/icons/technologies/mongodbsimple.svg b/apps/site/public/icons/technologies/mongodbsimple.svg new file mode 100644 index 0000000000..ebe9937d6b --- /dev/null +++ b/apps/site/public/icons/technologies/mongodbsimple.svg @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/apps/site/public/icons/technologies/mysqlsimple.svg b/apps/site/public/icons/technologies/mysqlsimple.svg new file mode 100644 index 0000000000..953749db04 --- /dev/null +++ b/apps/site/public/icons/technologies/mysqlsimple.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/site/public/illustrations/client/client_0.svg b/apps/site/public/illustrations/client/client_0.svg new file mode 100644 index 0000000000..1f56717e05 --- /dev/null +++ b/apps/site/public/illustrations/client/client_0.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/public/illustrations/client/client_0_light.svg b/apps/site/public/illustrations/client/client_0_light.svg new file mode 100644 index 0000000000..51444c8f8a --- /dev/null +++ b/apps/site/public/illustrations/client/client_0_light.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/public/illustrations/client/client_1.svg b/apps/site/public/illustrations/client/client_1.svg new file mode 100644 index 0000000000..998fb14935 --- /dev/null +++ b/apps/site/public/illustrations/client/client_1.svg @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/public/illustrations/client/client_1_light.svg b/apps/site/public/illustrations/client/client_1_light.svg new file mode 100644 index 0000000000..b48691cb92 --- /dev/null +++ b/apps/site/public/illustrations/client/client_1_light.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx index 16b74e5dfa..42ee61664f 100644 --- a/apps/site/src/app/client/page.tsx +++ b/apps/site/src/app/client/page.tsx @@ -5,83 +5,162 @@ import { } from "../../lib/blog-metadata"; import { Action, Button, Card } from "@prisma/eclipse"; import API from "@/components/client/api"; +import { CardSection } from "@/components/homepage/card-section/card-section"; +import { cn } from "@/lib/cn"; +import { Technology } from "@/components/client/technology"; export const metadata: Metadata = { title: SITE_HOME_TITLE, description: SITE_HOME_DESCRIPTION, }; -const migrateSteps: HeroCodeStep[] = [ - { - title: "Creating a new model", - migrateFileName: "init/20210211160000_init/migration.sql", - schema: `model User { - id Int @id @default(autoincrement()) - email String @unique - name String? -}`.trim(), - migrateFileContents: `-- CreateTable -CREATE TABLE "User" ( - "id" SERIAL NOT NULL, - "email" TEXT NOT NULL, - "name" TEXT, - - PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User.email_unique" IN "User"("email");`.trim(), - arrowOffset: { - x: -60, - y: 0, - rotation: 0, +const databases = { + title: "Supported Databases", + list: [ + { + name: "PostgreSQL", + icon: "/icons/companies/postgres.svg", + url: "/", + }, + { + name: "MySQL", + icon: "/icons/technologies/mysqlsimple.svg", + url: "/", + }, + { + name: "MariaDB", + icon: "/icons/technologies/mariadb.svg", + url: "/", + }, + { + name: "SQLite", + icon: "/icons/companies/sqlite.svg", + url: "/", + }, + { + name: "SQL Server", + icon: "/icons/companies/sqlserver.svg", + url: "/", + }, + { + name: "CockroachDB", + icon: "/icons/companies/cockroachdb.svg", + url: "/", + }, + { + name: "PlanetScale", + icon: "/icons/companies/planetscale.svg", + url: "/", + }, + { + name: "MongoDB", + icon: "/icons/technologies/mongodbsimple.svg", + url: "/", + }, + ], +}; +const frameworks = { + title: "Selected Frameworks", + description: + "Easy to integrate into your framework of choice, Prisma simplifies database access, saves repetitive CRUD boilerplate and increases type safety.", + list: [ + { + name: "React", + icon: "/icons/technologies/react.svg", + url: "/react", + }, + { + name: "Next.js", + icon: "/icons/technologies/nextjs.svg", + url: "/nextjs", + }, + { + name: "NestJS", + icon: "/icons/technologies/nestjs.svg", + url: "/nestjs", + }, + { + name: "Apollo", + icon: "/icons/technologies/apollo.svg", + url: "/apollo", + }, + { + name: "Hapi", + icon: "/icons/technologies/hapi.svg", + url: "/hapi", }, + { + name: "GraphQL", + icon: "/icons/technologies/graphql.svg", + url: "/graphql", + }, + { + name: "ExpressJS", + icon: "/icons/technologies/express.svg", + url: "/express", + }, + { + name: "Redwood", + icon: "/icons/technologies/redwoodjs.svg", + url: "/redwood", + }, + ], +}; +const twoCol = [ + { + content: ( + <> +

+ Autocomplete your way to success +

+

+ The best code is the code that writes itself. Prisma Client gives you + a fantastic autocomplete experience so you can move quickly and be + sure you don't write an invalid query. Our obsession with type safety + means you can rest assured that your code works as expected, every + time. +

+ + ), + imageUrl: "/illustrations/client/client_0", + imageAlt: "Autocomplete your way to success", + mobileImageUrl: null, + mobileImageAlt: null, + logos: null, + useDefaultLogos: false, + visualPosition: "right" as const, + visualType: "image" as const, }, { - title: "Adding a model", - migrateFileName: "init/20210325160100_add_post/migration.sql", - schema: ` -model User { - id Int @id @default(autoincrement()) - email String @unique - name String? -+ posts Post[] -} -+ -+model Post { -+ id Int @id @default(autoincrement()) -+ created DateTime @default(now()) -+ title String @db.VarChar(70) -+ content String -+ author User? @relation(fields: [authorId], references: [id]) -+ authorId Int -+}`.trim(), - migrateFileContents: ` --- CreateTable -CREATE TABLE "Post" ( - "id" SERIAL NOT NULL, - "created" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "title" VARCHAR(70) NOT NULL, - "content" TEXT NOT NULL, - "authorId" INTEGER NOT NULL, - PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "Post" ADD FOREIGN KEY ("authorId") REFERENCES -"User"("id") ON DELETE CASCADE ON UPDATE CASCADE;`.trim(), - arrowOffset: { - x: 190, - y: -20, - rotation: -70, - }, + content: ( + <> +

+ Fully type-safe raw SQL +

+

+ Execute SQL queries directly against your database without losing the + benefits of Prisma’s type-checking and auto-completion. TypedSQL + leverages the capabilities of Prisma Client to write raw SQL queries + that are type-checked at compile time. +

+ + ), + imageUrl: "/illustrations/client/client_1", + imageAlt: "Fully type-safe raw SQL", + mobileImageUrl: null, + mobileImageAlt: null, + logos: null, + useDefaultLogos: false, + noShadow: true, + visualPosition: "left" as const, + visualType: "image" as const, }, ]; export default function Client() { return (
-
+
@@ -99,249 +178,127 @@ export default function Client() { API tailored specifically for your app.

-
+
-
-
-
- -
- - - -

- Auto-generated -

+
+
+ +
+
+
+
+
+

+ Works with your favourite +
+ databases and framework +

+ +
+ {databases.title} +
+
+ {databases.list.map((db) => ( + + + {db.name} + + + ))}
-

- Migrations are automatically generated so you don't have to - write the SQL by hand. -

- -
- - - -

- Deterministic/ Repeatable -

+ +
+
+ {frameworks.title} +
+

+ {frameworks.description} +

-

- Migrate generates SQL migrations, ensuring migrations will - always result in the same database schema across environments. -

-
- -
- - - -

- Customizable -

+
+ {frameworks.list.map((fw) => ( + + + {fw.name} + + + ))}
-

- Generated SQL migrations can be fully customized giving you full - control over the exact changes. -

+
+ + +
-
-
-
-
-
-
-
- Iteration +
+
+
+ Prisma Studio
-

- Fast in development +

+ Visual database browser

-
    -
  • - -
    -

    - Prototype fast without migrations -

    -

    - While prototyping you can create the database schema - quickly using the prisma db push command without creating - migrations. -

    -
    -
  • - -
  • - -
    -

    - Integrated seeding -

    -

    - Quickly seed your database with data by defining a seed - script in JavaScript, TypeScript or Shell. -

    -
    -
  • -
  • - -
    -

    - Smart problem resolution -

    -

    - Migrate detects database schema drift and assists you in - resolving them. -

    -
    -
  • -
+

+ Prisma Studio is the easiest way to explore and manipulate data + in your Prisma projects. Understand your data by browsing across + tables, filter, paginate, traverse relations and edit your data + with safety. +

+
-
-
- Deployment +
+
+ Prisma Migrate
-

- Reliable in Production +

+ Hassle-free migrations

-
    -
  • - -
    -

    - Dedicated production workflows -

    -

    - Migrate supports dedicated workflows for carrying out - migrations safely in production. -

    -
    -
  • -
  • - -
    -

    - CI/CD Integration -

    -

    - Migrate can be integrated into CI/CD pipelines, e.g. - GitHub Actions, to automate applying migrations before - deployment. -

    -
    -
  • -
  • - -
    -

    - Conflict detection and resolution -

    -

    - Migrate keeps track of applied migrations and provides - tools to detect and resolve conflicts and drifts between - migrations and the database schema. -

    -
    -
  • -
-
-
-
-
-
-
-
- {/* Card 1 */} - -
- - - -

- Seamless integration with Prisma Client -

-
-

- When using Prisma Migrate with Prisma Client, schema changes are - type checked in your application code. This eliminates errors - that arise when database schema changes require changes to the - application code. -

-
- - {/* Card 2 */} - -
- - - -

- Declarative data modelling -

-
-

- Prisma Migrate generates migrations based on changes in the - Prisma schema – a human-readable declarative definition of your - database schema. This allows you to focus on your desired - database schema rather than the steps to get there. -

-
- - {/* Card 3 */} - -
- - - -

- Version control for your database -

-
-

- With Prisma Migrate, generated migrations are tracked in your - Git repository, allowing you to make changes to your database - schema in tandem with your application code. +

+ Prisma Migrate auto-generates SQL migrations from your Prisma + schema. These migration files are fully customizable, giving you + full control and ultimate flexibility — from local development + to production environments.

-
- - {/* Card 4 */} - -
- - - -

- Streamlined collaboration -

-
-

- With Prisma Migrate, generated migrations are tracked in your - Git repository, allowing you to make changes to your database - schema in tandem with your application code. -

-
- - {/* Card 5 */} - -
- - - -

- Bring your own project -

-
-

- Prisma Migrate can be adopted in any existing project that uses - PostgreSQL, MySQL, MariaDB, SQL Server, CockroachDB or SQLite. -

-
+ +
diff --git a/apps/site/src/components/client/api.tsx b/apps/site/src/components/client/api.tsx index a9ea9913fa..f829a64942 100644 --- a/apps/site/src/components/client/api.tsx +++ b/apps/site/src/components/client/api.tsx @@ -179,7 +179,7 @@ const API = () => { return (
-
+
@@ -252,7 +252,7 @@ const API = () => {
{}
-
+
{contentBlocks}
diff --git a/apps/site/src/components/client/technology.tsx b/apps/site/src/components/client/technology.tsx new file mode 100644 index 0000000000..ab24464528 --- /dev/null +++ b/apps/site/src/components/client/technology.tsx @@ -0,0 +1,36 @@ +"use client"; +import { + Button, + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@prisma/eclipse"; +import { useState } from "react"; + +export const Technology = ({ + children, + text, + url, +}: { + children: React.ReactNode; + text: string; + url?: string; +}) => { + return ( + + + + + + {text} + + + ); +}; diff --git a/apps/site/src/components/homepage/card-section/card-section.tsx b/apps/site/src/components/homepage/card-section/card-section.tsx index a7b4c70090..6e0c780af7 100644 --- a/apps/site/src/components/homepage/card-section/card-section.tsx +++ b/apps/site/src/components/homepage/card-section/card-section.tsx @@ -15,6 +15,7 @@ interface TwoColumnItem { useDefaultLogos: boolean; visualPosition: "left" | "right"; visualType: "logoGrid" | "image"; + noShadow?: boolean; } interface CardSectionProps { @@ -62,7 +63,10 @@ export const CardSection = ({ cardSection }: CardSectionProps) => { {item.visualType === "image" && item.imageUrl && ( <> Date: Wed, 25 Mar 2026 21:49:22 +0000 Subject: [PATCH 4/8] Add spaces to icons --- apps/site/src/app/client/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx index 42ee61664f..ff5e644fde 100644 --- a/apps/site/src/app/client/page.tsx +++ b/apps/site/src/app/client/page.tsx @@ -254,11 +254,11 @@ export default function Client() {
@@ -278,7 +278,7 @@ export default function Client() {

@@ -296,7 +296,7 @@ export default function Client() {

From 52ff10aa0b51d2ca4fa5f9ac89bbdb16e72244f3 Mon Sep 17 00:00:00 2001 From: Carla Goncalves Date: Wed, 25 Mar 2026 21:50:05 +0000 Subject: [PATCH 5/8] Update spacings --- apps/site/src/app/client/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx index ff5e644fde..861ed78ebd 100644 --- a/apps/site/src/app/client/page.tsx +++ b/apps/site/src/app/client/page.tsx @@ -179,7 +179,7 @@ export default function Client() {

-
+
From 236483dccda5105238ef279eabfec1ccabed67cc Mon Sep 17 00:00:00 2001 From: Carla Goncalves Date: Mon, 30 Mar 2026 12:01:09 +0100 Subject: [PATCH 6/8] Update select trigger for all instances --- apps/site/src/app/pricing/pricing-hero-plans.tsx | 16 ++++++++++------ apps/site/src/components/client/api.tsx | 9 +++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/site/src/app/pricing/pricing-hero-plans.tsx b/apps/site/src/app/pricing/pricing-hero-plans.tsx index a882dfb451..363bafbf8a 100644 --- a/apps/site/src/app/pricing/pricing-hero-plans.tsx +++ b/apps/site/src/app/pricing/pricing-hero-plans.tsx @@ -70,7 +70,7 @@ export function PricingHeroPlans({ - + {Object.entries(symbols).map(([code, symbol]) => ( {code} {symbol} @@ -88,7 +88,9 @@ export function PricingHeroPlans({
{highlighted && ( @@ -129,7 +131,8 @@ export function PricingHeroPlans({

{plan.price[currency]} - { ' ' } / month + {" "} + / month

diff --git a/apps/site/src/components/client/api.tsx b/apps/site/src/components/client/api.tsx index f829a64942..461848c786 100644 --- a/apps/site/src/components/client/api.tsx +++ b/apps/site/src/components/client/api.tsx @@ -49,7 +49,7 @@ const icons: any = { const renderItem = (item: any) => { return ( -
+
{icons[item.value]}   {item.label}
@@ -194,6 +194,7 @@ const API = () => {
{ if (value) { handleChange( From a9ba05fc47ad1bac6bb778d99ab2fe531435320a Mon Sep 17 00:00:00 2001 From: Carla Goncalves Date: Mon, 30 Mar 2026 16:58:43 +0100 Subject: [PATCH 8/8] Update links for client page --- apps/site/src/app/client/page.tsx | 51 ++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/apps/site/src/app/client/page.tsx b/apps/site/src/app/client/page.tsx index 861ed78ebd..48210db38e 100644 --- a/apps/site/src/app/client/page.tsx +++ b/apps/site/src/app/client/page.tsx @@ -109,10 +109,15 @@ const frameworks = { const twoCol = [ { content: ( - <> -

- Autocomplete your way to success -

+
+
+
+ Editor Integration +
+

+ Autocomplete your way to success +

+

The best code is the code that writes itself. Prisma Client gives you a fantastic autocomplete experience so you can move quickly and be @@ -120,7 +125,10 @@ const twoCol = [ means you can rest assured that your code works as expected, every time.

- + +
), imageUrl: "/illustrations/client/client_0", imageAlt: "Autocomplete your way to success", @@ -128,22 +136,30 @@ const twoCol = [ mobileImageAlt: null, logos: null, useDefaultLogos: false, - visualPosition: "right" as const, + visualPosition: "left" as const, visualType: "image" as const, }, { content: ( - <> -

- Fully type-safe raw SQL -

+
+
+
+ TypedSQL +
+

+ Fully type-safe raw SQL +

+

Execute SQL queries directly against your database without losing the benefits of Prisma’s type-checking and auto-completion. TypedSQL leverages the capabilities of Prisma Client to write raw SQL queries that are type-checked at compile time.

- + +
), imageUrl: "/illustrations/client/client_1", imageAlt: "Fully type-safe raw SQL", @@ -152,7 +168,7 @@ const twoCol = [ logos: null, useDefaultLogos: false, noShadow: true, - visualPosition: "left" as const, + visualPosition: "right" as const, visualType: "image" as const, }, ]; @@ -276,7 +292,7 @@ export default function Client() { tables, filter, paginate, traverse relations and edit your data with safety.

- @@ -294,8 +310,13 @@ export default function Client() { full control and ultimate flexibility — from local development to production environments.

-