Skip to content

feat: DR-7728 prisma with x#7698

Open
carlagn wants to merge 9 commits intomainfrom
feat/DR-7728-prisma-with-x
Open

feat: DR-7728 prisma with x#7698
carlagn wants to merge 9 commits intomainfrom
feat/DR-7728-prisma-with-x

Conversation

@carlagn
Copy link
Contributor

@carlagn carlagn commented Mar 24, 2026

Summary by CodeRabbit

  • New Features

    • Added "Prisma with" landing pages for Next.js, NestJS, and Remix with tabbed code examples, featured resources, quote and community sections
    • New reusable UI: post cards, author avatar group, and quote components; Prisma‑with layout and section components (hero, why, how, resources, quote, community)
  • Navigation Updates

    • Main nav labels and targets updated ("Get started" → "Prisma Partners", "Partners" → "Docs")
  • Improvements

    • Theme auto-detection with persisted preference; new hero spacing utility; homepage metadata source updated
  • Chores

    • Added syntax-highlighting/theme assets and CSS refinements

@vercel
Copy link

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Mar 25, 2026 1:45pm
docs Ready Ready Preview, Comment Mar 25, 2026 1:45pm
eclipse Ready Ready Preview, Comment Mar 25, 2026 1:45pm
site Ready Ready Preview, Comment Mar 25, 2026 1:45pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 24, 2026

Walkthrough

Replaces local blog card UI with a shared PostCard, adds shared UI components (PostCard, Quote, AuthorAvatarGroup), introduces a data-driven "prisma-with" system (JSON pages, layout, sections, README), adds shiki-based highlighter and theme, updates site layout/navigation/theme init and dependencies, and adjusts button variants and global styles.

Changes

Cohort / File(s) Summary
Shared UI components
packages/ui/src/components/post-card.tsx, packages/ui/src/components/author-avatar-group.tsx, packages/ui/src/components/quote.tsx, packages/ui/src/styles/globals.css
Added PostCard, AuthorAvatarGroup, and Quote components plus a minor CSS tweak (.stretch-display font-variation-settings). Introduced PostCardItem, AuthorProfile types and exports.
Blog → shared UI mapping
apps/blog/src/components/PostCard.tsx
Replaced inline blog card rendering with delegation to @prisma-docs/ui's PostCard, mapping post fields (tags, date, title/excerpt, author → profiles, image paths, nullable badge, formatted date).
Prisma-with pages & data
apps/site/src/app/(prisma-with)/nextjs/page.tsx, apps/site/src/app/(prisma-with)/nestjs/page.tsx, apps/site/src/data/prisma-with/*.json
Added new pages for Next.js and NestJS and three JSON content files (Next.js, NestJS, Remix) providing structured page data for the prisma-with system.
Prisma-with components & layout
apps/site/src/components/prisma-with/layout.tsx, apps/site/src/components/prisma-with/index.ts, apps/site/src/components/prisma-with/*.tsx, apps/site/src/components/prisma-with/README.md
Added PrismaWithLayout, barrel export, README, and section components: Hero, Why, How (async code highlighting), Resources (uses shared PostCard), QuoteSection, CommunitySection.
Code highlighting utility
apps/site/src/lib/shiki_prisma.ts
New Shiki theme prisma-dark and exported async prisma_highlighter configured for selected languages.
HowSection code rendering
apps/site/src/components/prisma-with/how-section.tsx
New async HowSection that highlights code examples using prisma_highlighter and renders tabbed HTML content with optional highlighted code panes.
Site layout, metadata, theme init, styles
apps/site/src/app/layout.tsx, apps/site/src/app/(index)/page.tsx, apps/site/src/app/og/image.png/route.tsx, apps/site/src/app/global.css
Switched metadata imports to site-metadata, added client-side theme init script, swapped FontAwesome script for kit from @prisma/eclipse, adjusted nav entries/links, added .hero utility and small CSS reformatting.
Site utilities & dependencies
apps/site/package.json, apps/site/src/lib/cn.ts
Updated package.json dependencies (added shiki, html-react-parser, tailwind-merge, posthog-js, etc.) and added cn re-export of twMerge.
Eclipse button & globals
packages/eclipse/src/components/button.tsx, packages/eclipse/src/styles/globals.css
Consolidated buttonVariants into a cva(...) with size variant and defaultVariants; adjusted variant styles and rendering branch (anchor vs button). Reformatted globals, added --color-disabled, and tweaked a dark theme variable.
Resources & community rendering
apps/site/src/components/prisma-with/resources-section.tsx, apps/site/src/components/prisma-with/community-section.tsx, apps/site/src/components/prisma-with/quote-section.tsx
New components rendering card grids; ResourcesSection leverages the shared PostCard for cards.
Misc site content & docs
apps/site/src/components/prisma-with/README.md, multiple apps/site/src/data/prisma-with/*.json
Added README documenting the prisma-with layout and multiple static JSON content files for tech pages.
Minor packaging/style edits
packages/ui/src/styles/globals.css, apps/site/src/app/og/image.png/route.tsx
Small style adjustment and JSX formatting-only changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title is vague and doesn't clearly describe the main changes. 'prisma with x' is a placeholder-like phrase that fails to communicate what was actually implemented. Replace with a specific title like 'feat: Add Prisma integration pages for Next.js, NestJS, and Remix' to clearly indicate the primary deliverable.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Mar 25, 2026, 1:51 PM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
packages/eclipse/src/styles/globals.css (1)

89-89: Identical --color-disabled in both themes may reduce contrast in dark mode.

Both light and dark themes use #4a5568 (gray-600) for --color-disabled. Against the dark theme's #030712 background, this yields roughly 4.5:1 contrast—acceptable for large text but potentially borderline for smaller UI elements. Consider using a lighter shade (e.g., #718096 or #9ca3af) in dark mode for better visibility.

Also applies to: 229-229

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/eclipse/src/styles/globals.css` at line 89, The disabled color
variable --color-disabled is identical in both themes and is too low-contrast on
the dark background; update the dark-theme definition of --color-disabled (the
declaration that currently sets --color-disabled at the dark-theme block around
the second occurrence) to a lighter gray (e.g., `#718096` or `#9ca3af`) instead of
`#4a5568`, ensure you only change the dark-theme scope (leave the light theme
value intact), and verify the resulting contrast ratio meets accessibility
guidelines for small UI text.
packages/eclipse/src/components/button.tsx (1)

24-30: Minor: Double spaces in size variant class strings.

Lines 27 and 29 have double spaces (px-3 h-element-2xl and px-4 h-element-4xl). While Tailwind handles this gracefully, it's inconsistent with the other size variants.

🧹 Proposed fix
       size: {
         lg: "px-2 h-element-lg type-text-sm-strong",
         xl: "px-3 h-element-xl type-text-sm-strong",
-        "2xl": "px-3  h-element-2xl type-text-sm-strong",
+        "2xl": "px-3 h-element-2xl type-text-sm-strong",
         "3xl": "px-4 h-element-3xl type-text-sm-strong",
-        "4xl": "px-4  h-element-4xl type-heading-md",
+        "4xl": "px-4 h-element-4xl type-heading-md",
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/eclipse/src/components/button.tsx` around lines 24 - 30, The size
variant object in Button (the size property in
packages/eclipse/src/components/button.tsx) contains inconsistent double spaces
in the class strings for keys "2xl" and "4xl" ("px-3  h-element-2xl" and "px-4 
h-element-4xl"); update those two entries to remove the extra space so all size
classes match the single-space formatting used by the other variants in the size
object.
apps/site/src/app/(prisma-with)/nextjs/page.tsx (2)

190-190: Minor: Prefer in operator over Object.keys().includes().

Object.keys(code_obj).includes(body.value) creates an intermediate array. Using the in operator is more direct and slightly more efficient.

♻️ Simpler check
-                          {Object.keys(code_obj).includes(body.value) && (
+                          {body.value in code_obj && (
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx at line 190, Replace the
costly Object.keys(...).includes(...) check with the in operator: instead of
Object.keys(code_obj).includes(body.value) use a direct property presence check
like body.value in code_obj; update the JSX conditional that references code_obj
and body.value accordingly to avoid creating the intermediate array and keep
behavior unchanged.

80-95: Array indexing on data.hero.btns assumes at least two buttons exist.

Lines 80 and 89 directly access btns[0] and btns[1] without bounds checking. If the JSON structure changes to have fewer buttons, this will throw at runtime.

Consider adding optional chaining or restructuring:

♻️ Safer button rendering
          <div className="flex gap-4">
-            <Button variant="ppg" size="3xl" href={data.hero.btns[0].url}>
-              <span>{data.hero.btns[0].label}</span>
-              {data.hero.btns[0].icon && (
-                <i className={cn("ml-2", data.hero.btns[0].icon)} />
-              )}
-            </Button>
-            <Button
-              variant="default-stronger"
-              size="3xl"
-              href={data.hero.btns[1].url}
-            >
-              <span>{data.hero.btns[1].label}</span>
-              {data.hero.btns[1].icon && (
-                <i className={cn("ml-2", data.hero.btns[1].icon)} />
-              )}
-            </Button>
+            {data.hero.btns.map((btn, idx) => (
+              <Button
+                key={btn.url}
+                variant={idx === 0 ? "ppg" : "default-stronger"}
+                size="3xl"
+                href={btn.url}
+              >
+                <span>{btn.label}</span>
+                {btn.icon && <i className={cn("ml-2", btn.icon)} />}
+              </Button>
+            ))}
          </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx around lines 80 - 95, The
code directly indexes data.hero.btns at btns[0] and btns[1] in the Button
rendering (inside page.tsx), which will throw if less than two buttons are
provided; update the rendering to safely handle missing items by either (a)
checking existence with optional chaining/guards before accessing
data.hero.btns[0] and data.hero.btns[1], or (b) iterating over data.hero.btns
(e.g., map) and rendering a Button for each entry, ensuring you reference
properties (label, url, icon) only when the item exists; apply this change to
the two Button usages so Button never receives undefined props.
packages/ui/src/components/quote.tsx (1)

38-45: Separator renders without a preceding title when only company is provided.

If author.title is undefined but author.company is set, the Separator will render as the first element in the title row, creating a visual artifact (a dangling vertical line before the company name).

Consider guarding the Separator so it only appears when both values exist:

♻️ Proposed fix
              {author.title && (
                <span className="font-[600] text-2xs uppercase">
                  {author.title}
                </span>
              )}
-              {author.company && (
+              {author.title && author.company && (
                <>
                  <Separator orientation="vertical" className="mx-1 h-3.5" />
-                  <span className="font-[400] text-xs uppercase text-foreground-ppg">
-                    {author.company}
-                  </span>
                </>
              )}
+              {author.company && (
+                <span className="font-[400] text-xs uppercase text-foreground-ppg">
+                  {author.company}
+                </span>
+              )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/quote.tsx` around lines 38 - 45, The Separator is
rendering even when author.title is missing, causing a dangling vertical line;
update the JSX in the quote component so the Separator is only rendered when
both author.title and author.company exist (i.e., change the condition around
Separator to check author.title && author.company rather than just
author.company), leaving the company <span className="font-[400] text-xs
uppercase text-foreground-ppg">{author.company}</span> to render independently
when only company is present; locate the fragment that contains Separator and
the company span in the Quote component and adjust the conditional rendering
accordingly.
apps/blog/src/components/PostCard.tsx (1)

52-61: Redundant date conversion on line 55.

post.date is already a string. You're converting it to a Date, then immediately back to an ISO string via toISOString(). Looking at formatDate in apps/blog/src/lib/format.ts, it already accepts an ISO string directly and handles the parsing internally.

♻️ Simplify the date formatting
  const sharedPost: SharedPostCardItem = {
    url: post.url,
    title: post.title,
-    date: formatDate(new Date(post.date).toISOString()),
+    date: formatDate(post.date),
    excerpt: post.excerpt,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/blog/src/components/PostCard.tsx` around lines 52 - 61, The date
conversion is redundant: in the SharedPostCardItem construction (sharedPost),
pass post.date directly to formatDate instead of new
Date(post.date).toISOString(); update the line in the sharedPost object from
date: formatDate(new Date(post.date).toISOString()) to date:
formatDate(post.date) so formatDate (in apps/blog/src/lib/format.ts) can parse
the ISO string itself and types remain consistent.
apps/site/src/lib/shiki_prisma.ts (1)

150-200: Mix of CSS variables and hardcoded hex colors may cause theme inconsistency.

Most token colors use CSS variables (e.g., var(--color-foreground-neutral-weak)), but several scopes use hardcoded hex values:

  • Line 153: #f92672 (tags)
  • Line 165: #96E072 (attribute strings)
  • Line 174: #f92672 (template expressions)
  • Line 180: #D5CED9 (meta template)
  • Line 186: #7cb7ff (primitive types)
  • Line 198: #FC644D (invalid tokens)

These won't adapt if you ever introduce a light-mode variant of this theme. If that's intentional (dark-only theme), this is fine. Otherwise, consider defining corresponding CSS variables for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/lib/shiki_prisma.ts` around lines 150 - 200, The theme uses
hardcoded hex colors for several token scopes (e.g., "entity.name.tag",
"meta.tag.attributes string.quoted",
"punctuation.definition.template-expression", "meta.template.expression",
"support.type.primitive", "invalid") which breaks theme adaptability; replace
those hardcoded foreground values with CSS variables (e.g.,
var(--color-token-tag), var(--color-token-attr-string),
var(--color-token-template), var(--color-token-primitive),
var(--color-token-invalid)) in the corresponding objects and add those CSS
variables to your theme root (and provide light-mode variants if needed) so
colors follow the established variable-driven system and can be switched for
light/dark themes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/site/src/app/og/image.png/route.tsx`:
- Line 1: The imported constants SITE_HOME_TITLE and SITE_HOME_DESCRIPTION are
not used — instead hardcoded strings are passed to the PrismaOGImage component;
update the PrismaOGImage invocation to pass SITE_HOME_TITLE and
SITE_HOME_DESCRIPTION (replace the literal "Prisma Blog" and the inline
description) so the OG image uses the centralized metadata values from
SITE_HOME_TITLE and SITE_HOME_DESCRIPTION.

In `@apps/site/src/data/prisma-with/nextjs.json`:
- Around line 81-117: The JSON contains three blog objects whose "url" fields
are duplicates (all "/blog/post-slug"); update each "url" value to the correct,
unique post slugs for the entries titled "How to use Prisma ORM with Next.js",
"Next.js 15 + Prisma Postgres Auth Example", and "Next.js 15 Demo App with
Prisma Postgres" by replacing the placeholder "/blog/post-slug" in their
respective objects with the real permalink for each post so each "url" field is
unique and points to the correct article.
- Around line 131-175: Several community card entries use placeholder URLs
("url": "#") which must be replaced before launch; locate the JSON objects with
titles "Modern SaaS Starter Kit: next-forge", "Prisma in Next.js - My Fav Way to
Work with Databases", "Fullstack Form Builder", "t3 Stack", and "Blitz.js" and
replace each "url": "#" with the actual target link (or a config/constant
reference) or add a clear TODO/comment pointing to the issue/PR that will supply
the correct URL so the cards navigate to real resources.

In `@packages/ui/src/components/author-avatar-group.tsx`:
- Around line 28-41: The Avatar key currently uses author.name which can
collide; change the key in the authors.map render to a stable unique value
(preferably a unique identifier like author.id if available) and fall back to a
composite or the array index to guarantee uniqueness (e.g. use author.id ||
`${author.name}-${index}` or author.id ?? index) when rendering Avatar in the
authors.map callback to avoid duplicate key warnings; update the key prop where
Avatar is returned.

---

Nitpick comments:
In `@apps/blog/src/components/PostCard.tsx`:
- Around line 52-61: The date conversion is redundant: in the SharedPostCardItem
construction (sharedPost), pass post.date directly to formatDate instead of new
Date(post.date).toISOString(); update the line in the sharedPost object from
date: formatDate(new Date(post.date).toISOString()) to date:
formatDate(post.date) so formatDate (in apps/blog/src/lib/format.ts) can parse
the ISO string itself and types remain consistent.

In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx:
- Line 190: Replace the costly Object.keys(...).includes(...) check with the in
operator: instead of Object.keys(code_obj).includes(body.value) use a direct
property presence check like body.value in code_obj; update the JSX conditional
that references code_obj and body.value accordingly to avoid creating the
intermediate array and keep behavior unchanged.
- Around line 80-95: The code directly indexes data.hero.btns at btns[0] and
btns[1] in the Button rendering (inside page.tsx), which will throw if less than
two buttons are provided; update the rendering to safely handle missing items by
either (a) checking existence with optional chaining/guards before accessing
data.hero.btns[0] and data.hero.btns[1], or (b) iterating over data.hero.btns
(e.g., map) and rendering a Button for each entry, ensuring you reference
properties (label, url, icon) only when the item exists; apply this change to
the two Button usages so Button never receives undefined props.

In `@apps/site/src/lib/shiki_prisma.ts`:
- Around line 150-200: The theme uses hardcoded hex colors for several token
scopes (e.g., "entity.name.tag", "meta.tag.attributes string.quoted",
"punctuation.definition.template-expression", "meta.template.expression",
"support.type.primitive", "invalid") which breaks theme adaptability; replace
those hardcoded foreground values with CSS variables (e.g.,
var(--color-token-tag), var(--color-token-attr-string),
var(--color-token-template), var(--color-token-primitive),
var(--color-token-invalid)) in the corresponding objects and add those CSS
variables to your theme root (and provide light-mode variants if needed) so
colors follow the established variable-driven system and can be switched for
light/dark themes.

In `@packages/eclipse/src/components/button.tsx`:
- Around line 24-30: The size variant object in Button (the size property in
packages/eclipse/src/components/button.tsx) contains inconsistent double spaces
in the class strings for keys "2xl" and "4xl" ("px-3  h-element-2xl" and "px-4 
h-element-4xl"); update those two entries to remove the extra space so all size
classes match the single-space formatting used by the other variants in the size
object.

In `@packages/eclipse/src/styles/globals.css`:
- Line 89: The disabled color variable --color-disabled is identical in both
themes and is too low-contrast on the dark background; update the dark-theme
definition of --color-disabled (the declaration that currently sets
--color-disabled at the dark-theme block around the second occurrence) to a
lighter gray (e.g., `#718096` or `#9ca3af`) instead of `#4a5568`, ensure you only
change the dark-theme scope (leave the light theme value intact), and verify the
resulting contrast ratio meets accessibility guidelines for small UI text.

In `@packages/ui/src/components/quote.tsx`:
- Around line 38-45: The Separator is rendering even when author.title is
missing, causing a dangling vertical line; update the JSX in the quote component
so the Separator is only rendered when both author.title and author.company
exist (i.e., change the condition around Separator to check author.title &&
author.company rather than just author.company), leaving the company <span
className="font-[400] text-xs uppercase
text-foreground-ppg">{author.company}</span> to render independently when only
company is present; locate the fragment that contains Separator and the company
span in the Quote component and adjust the conditional rendering accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e45877d2-0b31-4359-9122-048be6b88fc4

📥 Commits

Reviewing files that changed from the base of the PR and between 1305845 and 044101d.

⛔ Files ignored due to path filters (6)
  • apps/site/public/icons/technologies/nextjs-light.svg is excluded by !**/*.svg
  • apps/site/public/icons/technologies/nextjs.svg is excluded by !**/*.svg
  • apps/site/public/icons/technologies/prisma.svg is excluded by !**/*.svg
  • apps/site/public/icons/technologies/prisma_light.svg is excluded by !**/*.svg
  • apps/site/public/illustrations/hero-grid.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • apps/blog/src/components/PostCard.tsx
  • apps/site/package.json
  • apps/site/src/app/(index)/page.tsx
  • apps/site/src/app/(prisma-with)/nextjs/page.tsx
  • apps/site/src/app/global.css
  • apps/site/src/app/layout.tsx
  • apps/site/src/app/og/image.png/route.tsx
  • apps/site/src/data/prisma-with/nextjs.json
  • apps/site/src/lib/cn.ts
  • apps/site/src/lib/shiki_prisma.ts
  • apps/site/src/lib/site-metadata.ts
  • packages/eclipse/src/components/button.tsx
  • packages/eclipse/src/styles/globals.css
  • packages/ui/src/components/author-avatar-group.tsx
  • packages/ui/src/components/post-card.tsx
  • packages/ui/src/components/quote.tsx
  • packages/ui/src/styles/globals.css

@@ -1,4 +1,4 @@
import { SITE_HOME_DESCRIPTION, SITE_HOME_TITLE } from "@/lib/blog-metadata";
import { SITE_HOME_DESCRIPTION, SITE_HOME_TITLE } from "@/lib/site-metadata";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Imported constants are unused—hardcoded values are passed instead.

You import SITE_HOME_DESCRIPTION and SITE_HOME_TITLE from @/lib/site-metadata, but the PrismaOGImage component receives hardcoded strings ("Prisma Blog" and the description literal). This means changes to the metadata constants won't reflect in the OG image.

🔗 Proposed fix to use the imported constants
   return new ImageResponse(
     <PrismaOGImage
-      title="Prisma Blog"
-      description="Guides, announcements, and articles about Prisma, ORMs, databases, and the data access layer."
+      title={SITE_HOME_TITLE}
+      description={SITE_HOME_DESCRIPTION}
     />,

Also applies to: 180-183

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/og/image.png/route.tsx` at line 1, The imported constants
SITE_HOME_TITLE and SITE_HOME_DESCRIPTION are not used — instead hardcoded
strings are passed to the PrismaOGImage component; update the PrismaOGImage
invocation to pass SITE_HOME_TITLE and SITE_HOME_DESCRIPTION (replace the
literal "Prisma Blog" and the inline description) so the OG image uses the
centralized metadata values from SITE_HOME_TITLE and SITE_HOME_DESCRIPTION.

Comment on lines +81 to +117
{
"image": "/images/blog/prisma-nextjs-orm.jpg",
"url": "/blog/post-slug",
"badge": "Release",
"date": "October 22, 2025",
"title": "How to use Prisma ORM with Next.js",
"description": "Learn how to build a fullstack Next.js 15 app with Prisma Postgres from scratch! This tutorial starts...",
"author": {
"name": "Johan Schmidt",
"avatar": "/images/authors/johan-schmidt.jpg"
}
},
{
"image": "/images/blog/nextjs-prisma-postgres-auth.jpg",
"badge": "Release",
"date": "October 22, 2025",
"url": "/blog/post-slug",
"title": "Next.js 15 + Prisma Postgres Auth Example",
"description": "This demo for a blogging application demonstrates how to build login and functionalit...",
"author": {
"name": "Johan Schmidt",
"avatar": "/images/authors/johan-schmidt.jpg"
}
},
{
"image": "/images/blog/nextjs-demo-app.jpg",
"badge": "Release",
"date": "October 22, 2025",
"url": "/blog/post-slug",
"title": "Next.js 15 Demo App with Prisma Postgres",
"description": "This demo for a blogging application demonstrates how to build login and functionalit...",
"author": {
"name": "Johan Schmidt",
"avatar": "/images/authors/johan-schmidt.jpg"
}
}
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Duplicate blog post URLs detected.

Lines 83, 97, and 109 all point to /blog/post-slug, which appears to be placeholder content. Ensure these are updated to actual post URLs before merging to production.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/data/prisma-with/nextjs.json` around lines 81 - 117, The JSON
contains three blog objects whose "url" fields are duplicates (all
"/blog/post-slug"); update each "url" value to the correct, unique post slugs
for the entries titled "How to use Prisma ORM with Next.js", "Next.js 15 +
Prisma Postgres Auth Example", and "Next.js 15 Demo App with Prisma Postgres" by
replacing the placeholder "/blog/post-slug" in their respective objects with the
real permalink for each post so each "url" field is unique and points to the
correct article.

Comment on lines +28 to +41
{authors.map((author, index) =>
author.imageSrc ? (
<Avatar
key={author.name}
format="image"
src={author.imageSrc}
alt={author.name}
size="lg"
className={
index > 0 ? "-ml-1.5 border border-background-default" : ""
}
/>
) : null,
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Using author.name as key may cause collisions.

If two authors share the same name (however unlikely), React will warn about duplicate keys and may exhibit unexpected rendering behavior. Consider using the array index or a unique identifier if available:

🔑 Proposed fix using index
       {authors.map((author, index) =>
         author.imageSrc ? (
           <Avatar
-            key={author.name}
+            key={`${author.name}-${index}`}
             format="image"
             src={author.imageSrc}
             alt={author.name}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{authors.map((author, index) =>
author.imageSrc ? (
<Avatar
key={author.name}
format="image"
src={author.imageSrc}
alt={author.name}
size="lg"
className={
index > 0 ? "-ml-1.5 border border-background-default" : ""
}
/>
) : null,
)}
{authors.map((author, index) =>
author.imageSrc ? (
<Avatar
key={`${author.name}-${index}`}
format="image"
src={author.imageSrc}
alt={author.name}
size="lg"
className={
index > 0 ? "-ml-1.5 border border-background-default" : ""
}
/>
) : null,
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/author-avatar-group.tsx` around lines 28 - 41, The
Avatar key currently uses author.name which can collide; change the key in the
authors.map render to a stable unique value (preferably a unique identifier like
author.id if available) and fall back to a composite or the array index to
guarantee uniqueness (e.g. use author.id || `${author.name}-${index}` or
author.id ?? index) when rendering Avatar in the authors.map callback to avoid
duplicate key warnings; update the key prop where Avatar is returned.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/site/src/app/(prisma-with)/nextjs/page.tsx (1)

79-95: Render the hero CTAs from the data array.

This section assumes data.hero.btns always has exactly two items. A one-button payload will throw at render time, and any extra CTA is silently ignored. Mapping the array keeps the hero aligned with the rest of the JSON-driven page.

Suggested refactor
-          <div className="flex gap-4 md:justify-start justify-center">
-            <Button variant="ppg" size="3xl" href={data.hero.btns[0].url}>
-              <span>{data.hero.btns[0].label}</span>
-              {data.hero.btns[0].icon && (
-                <i className={cn("ml-2", data.hero.btns[0].icon)} />
-              )}
-            </Button>
-            <Button
-              variant="default-stronger"
-              size="3xl"
-              href={data.hero.btns[1].url}
-            >
-              <span>{data.hero.btns[1].label}</span>
-              {data.hero.btns[1].icon && (
-                <i className={cn("ml-2", data.hero.btns[1].icon)} />
-              )}
-            </Button>
-          </div>
+          <div className="flex gap-4 md:justify-start justify-center">
+            {data.hero.btns.map((btn, idx) => (
+              <Button
+                key={`${btn.label}-${btn.url}`}
+                variant={idx === 0 ? "ppg" : "default-stronger"}
+                size="3xl"
+                href={btn.url}
+              >
+                <span>{btn.label}</span>
+                {btn.icon && <i className={cn("ml-2", btn.icon)} />}
+              </Button>
+            ))}
+          </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx around lines 79 - 95, The
hard-coded two-Button render assumes data.hero.btns has exactly two items;
change the JSX in the hero section to map over data.hero.btns and render a
Button for each item (use data.hero.btns.map((btn, i) => ...)), supplying key
(e.g. btn.url or btn.label), href={btn.url}, size="3xl", and determine variant
by preferring btn.variant if present otherwise use i === 0 ? "ppg" :
"default-stronger"; preserve the icon rendering (btn.icon => <i
className={cn("ml-2", btn.icon)} />) and label span so any number of CTAs in
data.hero.btns are rendered safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx:
- Around line 241-243: The imageAlt value is hardcoded to "Post image" for each
card; update the card rendering (where imageSrc: card.image, imageAlt: "Post
image", badge: card.badge is set) to use a meaningful alt from the card data
(e.g., imageAlt: card.alt || card.title) and fall back to an empty string when
the image is purely decorative (e.g., imageAlt: card.alt ?? "") so each card
either provides descriptive alt text or an empty alt for decorative images.
- Around line 189-208: The wrapper div is rendered even when no snippet exists
for the active tab because the guard checks
Object.keys(code_obj).includes(body.value) only inside the padded/bordered
container; move that guard up to wrap the entire block so the container (and
CodeBlock) is not rendered when code_obj lacks body.value, i.e. check presence
before rendering the outer <div> that contains CodeBlock (references: code_obj,
body.value, CodeBlock, prisma_highlighter.codeToHtml), or alternatively
validate/throw earlier if every tab must have a snippet.
- Around line 35-39: The Page component currently treats params as a synchronous
object; update it to accept and await the async params pattern used by the App
Router by changing the prop type to { params: Promise<{ slug: string }> } (or
similar), then await the params inside the function (e.g., const { slug } =
await params) and pass slug to prisma.post.findUnique({ where: { slug } });
adjust the Page signature and the const post lookup accordingly in the Page
function.

---

Nitpick comments:
In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx:
- Around line 79-95: The hard-coded two-Button render assumes data.hero.btns has
exactly two items; change the JSX in the hero section to map over data.hero.btns
and render a Button for each item (use data.hero.btns.map((btn, i) => ...)),
supplying key (e.g. btn.url or btn.label), href={btn.url}, size="3xl", and
determine variant by preferring btn.variant if present otherwise use i === 0 ?
"ppg" : "default-stronger"; preserve the icon rendering (btn.icon => <i
className={cn("ml-2", btn.icon)} />) and label span so any number of CTAs in
data.hero.btns are rendered safely.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d40df2b-c68a-44f2-8599-c4c3c3e9ef2d

📥 Commits

Reviewing files that changed from the base of the PR and between 044101d and 4a3ebdf.

⛔ Files ignored due to path filters (1)
  • apps/site/public/photos/people/guillermo-rauch.jpeg is excluded by !**/*.jpeg
📒 Files selected for processing (2)
  • apps/site/src/app/(prisma-with)/nextjs/page.tsx
  • apps/site/src/data/prisma-with/nextjs.json
✅ Files skipped from review due to trivial changes (1)
  • apps/site/src/data/prisma-with/nextjs.json

Comment on lines +35 to +39
export default async function Page({ params }: { params: { slug: string } }) {
// Fetch the post based on slug
const post = await prisma.post.findUnique({
where: { slug: params.slug },
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In the official Next.js App Router docs, what is the current paramstype and access pattern for a dynamic routeapp/blog/[slug]/page.tsx page?

💡 Result:

In the official Next.js App Router documentation (as of March 2026), for a dynamic route at app/blog/[slug]/page.tsx, the params prop is typed as Promise<{ slug: string }> and must be awaited in an async Server Component page function. Example from the docs: export default async function Page({ params, }: { params: Promise<{ slug: string }> }) { const { slug } = await params; // Use slug... } You can also use the generated PageProps<'/blog/[slug]'> type helper for better inference: export default async function Page(props: PageProps<'/blog/[slug]'>) { const { slug } = await props.params; // Use slug... } This async params behavior was introduced in Next.js 15; earlier versions (14 and below) used synchronous { params: { slug: string } }. Since params is a Promise, the page component must be async and await it (or use React's use function). The value is { slug: string } for this single dynamic segment.

Citations:


Update the embedded code example to use the current Next.js async params pattern.

This sample teaches synchronous params access, but the current Next.js App Router documentation specifies that params is a Promise that must be awaited in page.tsx. Since this is copy-paste-facing content, it should reflect the current best practice (Next.js 15+):

Suggested update
-export default async function Page({ params }: { params: { slug: string } }) {
-  // Fetch the post based on slug
+export default async function Page({
+  params,
+}: {
+  params: Promise<{ slug: string }>
+}) {
+  const { slug } = await params
+
+  // Fetch the post based on slug
   const post = await prisma.post.findUnique({
-    where: { slug: params.slug },
+    where: { slug },
   })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default async function Page({ params }: { params: { slug: string } }) {
// Fetch the post based on slug
const post = await prisma.post.findUnique({
where: { slug: params.slug },
})
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const { slug } = await params
// Fetch the post based on slug
const post = await prisma.post.findUnique({
where: { slug },
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx around lines 35 - 39, The
Page component currently treats params as a synchronous object; update it to
accept and await the async params pattern used by the App Router by changing the
prop type to { params: Promise<{ slug: string }> } (or similar), then await the
params inside the function (e.g., const { slug } = await params) and pass slug
to prisma.post.findUnique({ where: { slug } }); adjust the Page signature and
the const post lookup accordingly in the Page function.

Comment on lines +241 to +243
imageSrc: card.image,
imageAlt: "Post image",
badge: card.badge,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use meaningful alt text for each post card image.

Every card passes the same "Post image" alt, so assistive tech gets identical, non-informative output for the whole grid. Please either source image-specific alt text from the data or pass "" when the artwork is decorative.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nextjs/page.tsx around lines 241 - 243, The
imageAlt value is hardcoded to "Post image" for each card; update the card
rendering (where imageSrc: card.image, imageAlt: "Post image", badge: card.badge
is set) to use a meaningful alt from the card data (e.g., imageAlt: card.alt ||
card.title) and fall back to an empty string when the image is purely decorative
(e.g., imageAlt: card.alt ?? "") so each card either provides descriptive alt
text or an empty alt for decorative images.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
apps/site/src/components/prisma-with/community-section.tsx (1)

31-32: Simplify redundant index conditions for class assignment.

idx >= 3 && idx === 3 and idx >= 3 && idx === 4 can be reduced to direct equality checks.

♻️ Suggested cleanup
-                idx >= 3 && idx === 3 && "md:col-start-2",
-                idx >= 3 && idx === 4 && "md:col-start-4",
+                idx === 3 && "md:col-start-2",
+                idx === 4 && "md:col-start-4",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/prisma-with/community-section.tsx` around lines 31 -
32, The class assignment uses redundant checks like "idx >= 3 && idx === 3" and
"idx >= 3 && idx === 4"; simplify these to direct equality checks by replacing
them with "idx === 3" and "idx === 4" respectively wherever the conditional
class logic for idx is applied (look for the JSX/array of class names that
includes those expressions in the community-section component).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/site/src/app/`(prisma-with)/nestjs/page.tsx:
- Around line 5-36: The codeExamples map only contains the "static-data" key, so
tabs that expect other examples render empty; update the codeExamples constant
to include entries for every tab key used by the tabbed UI (add keys matching
the tab body values such as the other example names used in the UI), keeping the
same string value shape as the existing "static-data" entry; locate the
codeExamples declaration in apps/site/src/app/(prisma-with)/nestjs/page.tsx and
add the missing keys (matching the tab identifiers) with appropriate code
strings so the tabbed section can render content for each tab.
- Around line 38-44: The page-level metadata object named metadata in page.tsx
currently references Next.js (title, description, alternates.canonical) and
should be updated to reflect NestJS instead; change the title to something like
"NestJS Database with Prisma | Next-Generation ORM for SQL Databases", adjust
the description to mention NestJS (e.g., "Prisma is a next-generation ORM for
Node.js & TypeScript and a great fit for NestJS apps..."), and update
alternates.canonical to the correct NestJS URL (replace the "/nextjs" path with
the appropriate "/nestjs" canonical path).

In `@apps/site/src/components/prisma-with/hero.tsx`:
- Around line 13-17: The Hero component assumes two CTAs but btns is typed as an
unconstrained Array — guard against missing entries or enforce a two-item tuple:
either change the btns type to a tuple like [FirstBtn, SecondBtn?] (making the
second optional) or add runtime checks before accessing data.btns[1].url /
data.btns[1].label; update all usages in hero.tsx (places accessing data.btns[1]
around the btns declaration and the render block at lines ~40-55) to use
optional chaining and conditional rendering (e.g., render second button only if
data.btns[1] exists) so no runtime crash occurs.

In `@apps/site/src/components/prisma-with/README.md`:
- Around line 11-22: In README.md update the fenced code block that lists the
prisma-with directory tree to include a language identifier (e.g., ```text) on
the opening fence so the block becomes a labeled code fence; locate the block
that begins with "prisma-with/" and replace the opening ``` with ```text to
satisfy markdown linting.

---

Nitpick comments:
In `@apps/site/src/components/prisma-with/community-section.tsx`:
- Around line 31-32: The class assignment uses redundant checks like "idx >= 3
&& idx === 3" and "idx >= 3 && idx === 4"; simplify these to direct equality
checks by replacing them with "idx === 3" and "idx === 4" respectively wherever
the conditional class logic for idx is applied (look for the JSX/array of class
names that includes those expressions in the community-section component).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58e354b6-26ae-4ba8-b962-bb14c235dede

📥 Commits

Reviewing files that changed from the base of the PR and between 4a3ebdf and 6876053.

📒 Files selected for processing (13)
  • apps/site/src/app/(prisma-with)/nestjs/page.tsx
  • apps/site/src/app/(prisma-with)/nextjs/page.tsx
  • apps/site/src/components/prisma-with/README.md
  • apps/site/src/components/prisma-with/community-section.tsx
  • apps/site/src/components/prisma-with/hero.tsx
  • apps/site/src/components/prisma-with/how-section.tsx
  • apps/site/src/components/prisma-with/index.ts
  • apps/site/src/components/prisma-with/layout.tsx
  • apps/site/src/components/prisma-with/quote-section.tsx
  • apps/site/src/components/prisma-with/resources-section.tsx
  • apps/site/src/components/prisma-with/why-section.tsx
  • apps/site/src/data/prisma-with/nestjs.json
  • apps/site/src/data/prisma-with/remix.json
✅ Files skipped from review due to trivial changes (4)
  • apps/site/src/components/prisma-with/index.ts
  • apps/site/src/data/prisma-with/nestjs.json
  • apps/site/src/data/prisma-with/remix.json
  • apps/site/src/components/prisma-with/resources-section.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/site/src/app/(prisma-with)/nextjs/page.tsx

Comment on lines +5 to +36
const codeExamples: Record<string, string> = {
"static-data": `// app/blog/[slug]/page.tsx
import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

// Return a list of 'params' to populate the [slug] dynamic segment
export async function generateStaticParams() {
const posts = await prisma.post.findMany()

return posts.map((post) => ({
slug: post.slug,
}))
}

// Multiple versions of this page will be statically generated
// using the 'params' returned by 'generateStaticParams'
export default async function Page({ params }: { params: { slug: string } }) {
// Fetch the post based on slug
const post = await prisma.post.findUnique({
where: { slug: params.slug },
})

// Simple demo rendering
return (
<div>
<h1>{post?.title || 'Post not found'}</h1>
<p>{post?.content || 'No content available'}</p>
</div>
)
}`,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

codeExamples is incomplete for the tab set, causing empty code areas.

This map only defines "static-data", but the tabbed section expects keys matching all tab body values. Unmatched tabs render without code content, which degrades the core walkthrough UX.

💡 Suggested fix
 const codeExamples: Record<string, string> = {
   "static-data": `...`,
+  "dynamic-data": `...`,
+  "server-actions": `...`,
+  "api-routes": `...`,
+  "client-components": `...`,
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nestjs/page.tsx around lines 5 - 36, The
codeExamples map only contains the "static-data" key, so tabs that expect other
examples render empty; update the codeExamples constant to include entries for
every tab key used by the tabbed UI (add keys matching the tab body values such
as the other example names used in the UI), keeping the same string value shape
as the existing "static-data" entry; locate the codeExamples declaration in
apps/site/src/app/(prisma-with)/nestjs/page.tsx and add the missing keys
(matching the tab identifiers) with appropriate code strings so the tabbed
section can render content for each tab.

Comment on lines +38 to +44
export const metadata: Metadata = {
title: "Next.js Database with Prisma | Next-Generation ORM for SQL Databases",
description:
"Prisma is a next-generation ORM for Node.js & TypeScript. It's the easiest way to build Next.js apps with MySQL, PostgreSQL & SQL Server databases.",
alternates: {
canonical: "https://www.prisma.io/nextjs",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Metadata appears copied from Next.js page instead of NestJS page.

Title and canonical currently reference Next.js (/nextjs), which is incorrect for this route and can hurt SEO/canonicalization.

💡 Suggested fix
 export const metadata: Metadata = {
-  title: "Next.js Database with Prisma | Next-Generation ORM for SQL Databases",
+  title: "NestJS Database with Prisma | Next-Generation ORM for SQL Databases",
   description:
-    "Prisma is a next-generation ORM for Node.js & TypeScript. It's the easiest way to build Next.js apps with MySQL, PostgreSQL & SQL Server databases.",
+    "Prisma is a next-generation ORM for Node.js & TypeScript. It's the easiest way to build NestJS apps with MySQL, PostgreSQL & SQL Server databases.",
   alternates: {
-    canonical: "https://www.prisma.io/nextjs",
+    canonical: "https://www.prisma.io/nestjs",
   },
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const metadata: Metadata = {
title: "Next.js Database with Prisma | Next-Generation ORM for SQL Databases",
description:
"Prisma is a next-generation ORM for Node.js & TypeScript. It's the easiest way to build Next.js apps with MySQL, PostgreSQL & SQL Server databases.",
alternates: {
canonical: "https://www.prisma.io/nextjs",
},
export const metadata: Metadata = {
title: "NestJS Database with Prisma | Next-Generation ORM for SQL Databases",
description:
"Prisma is a next-generation ORM for Node.js & TypeScript. It's the easiest way to build NestJS apps with MySQL, PostgreSQL & SQL Server databases.",
alternates: {
canonical: "https://www.prisma.io/nestjs",
},
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/`(prisma-with)/nestjs/page.tsx around lines 38 - 44, The
page-level metadata object named metadata in page.tsx currently references
Next.js (title, description, alternates.canonical) and should be updated to
reflect NestJS instead; change the title to something like "NestJS Database with
Prisma | Next-Generation ORM for SQL Databases", adjust the description to
mention NestJS (e.g., "Prisma is a next-generation ORM for Node.js & TypeScript
and a great fit for NestJS apps..."), and update alternates.canonical to the
correct NestJS URL (replace the "/nextjs" path with the appropriate "/nestjs"
canonical path).

Comment on lines +13 to +17
btns: Array<{
label: string;
icon?: string;
url: string;
}>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard CTA access or enforce tuple length to prevent runtime crashes.

The code assumes two buttons always exist, but btns is typed as an unconstrained array. Accessing data.btns[1].url/label will throw if only one CTA is provided.

🛠️ Suggested fix
 type HeroData = {
@@
-  btns: Array<{
-    label: string;
-    icon?: string;
-    url: string;
-  }>;
+  btns: [
+    {
+      label: string;
+      icon?: string;
+      url: string;
+    },
+    ...Array<{
+      label: string;
+      icon?: string;
+      url: string;
+    }>,
+  ];
 };
@@
-            <Button
-              variant="default-stronger"
-              size="3xl"
-              href={data.btns[1].url}
-            >
-              <span>{data.btns[1].label}</span>
-              {data.btns[1].icon && (
-                <i className={cn("ml-2", data.btns[1].icon)} />
-              )}
-            </Button>
+            {data.btns[1] && (
+              <Button
+                variant="default-stronger"
+                size="3xl"
+                href={data.btns[1].url}
+              >
+                <span>{data.btns[1].label}</span>
+                {data.btns[1].icon && (
+                  <i className={cn("ml-2", data.btns[1].icon)} />
+                )}
+              </Button>
+            )}

Also applies to: 40-55

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/prisma-with/hero.tsx` around lines 13 - 17, The Hero
component assumes two CTAs but btns is typed as an unconstrained Array — guard
against missing entries or enforce a two-item tuple: either change the btns type
to a tuple like [FirstBtn, SecondBtn?] (making the second optional) or add
runtime checks before accessing data.btns[1].url / data.btns[1].label; update
all usages in hero.tsx (places accessing data.btns[1] around the btns
declaration and the render block at lines ~40-55) to use optional chaining and
conditional rendering (e.g., render second button only if data.btns[1] exists)
so no runtime crash occurs.

Comment on lines +11 to +22
```
prisma-with/
├── layout.tsx # Main layout component
├── hero.tsx # Hero section with tech logos
├── why-section.tsx # "Why Prisma + Tech?" card grid
├── how-section.tsx # "How they fit together" with tabs & code
├── resources-section.tsx # Blog posts/resources cards
├── quote-section.tsx # Customer testimonial
├── community-section.tsx # Community examples
├── index.ts # Barrel exports
└── README.md # This file
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a language identifier to the fenced code block.

The block starting at Line 11 should specify a language (for example text) to satisfy markdown linting.

📝 Suggested fix
-```
+```text
 prisma-with/
 ├── layout.tsx              # Main layout component
 ...
 └── README.md              # This file
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 11-11: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/prisma-with/README.md` around lines 11 - 22, In
README.md update the fenced code block that lists the prisma-with directory tree
to include a language identifier (e.g., ```text) on the opening fence so the
block becomes a labeled code fence; locate the block that begins with
"prisma-with/" and replace the opening ``` with ```text to satisfy markdown
linting.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/ui/src/components/post-card.tsx (2)

27-27: Redundant variable aliasing.

isFeatured is simply assigned from featured without any transformation. You can use featured directly throughout the component to reduce cognitive overhead.

♻️ Suggested simplification
-  const isFeatured = featured;
-  const imageSizes = isFeatured ? "(min-width: 640px) 50vw, 100vw" : "384px";
+  const imageSizes = featured ? "(min-width: 640px) 50vw, 100vw" : "384px";

Then replace all isFeatured references with featured.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/post-card.tsx` at line 27, Remove the redundant
alias by deleting the const isFeatured = featured; line and replace all uses of
isFeatured in the component with the original prop/variable featured (e.g.,
update any conditional checks, classes, or JSX that reference isFeatured to use
featured instead). Ensure imports/props/destructuring that provide featured
remain unchanged and run tests/typechecks to confirm no remaining references to
isFeatured.

104-111: Heavy style overrides on Card component.

Using multiple !important overrides (rounded-none!, border-none!) to reset the Card's base styles is a design smell. If the featured layout consistently needs these resets, consider either:

  1. Adding a variant prop to the Card component (e.g., variant="flat")
  2. Using a simpler container element instead of Card when these styles aren't needed

This isn't blocking, but worth noting for future maintainability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/post-card.tsx` around lines 104 - 111, The Card
usage in the post-card component applies heavy "!important" style resets
(classes like "rounded-none!" and "border-none!") — change this by either (A)
updating the Card API to accept a new variant prop (e.g., Card variant="flat")
and map that variant to the flat styles inside the Card implementation, then
replace the inline "!important" classes on the Card component in this file with
variant="flat" and only the remaining needed layout classes via className, or
(B) if the Card base styles are not needed for this layout, swap the Card here
for a plain semantic container (e.g., a div) and move the layout classes
(cn(..., !vertical && "order-2 gap-0")) to that container; reference the Card
component usage and the local variables postBody and vertical when making the
change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/components/post-card.tsx`:
- Line 28: The computed but unused imageSizes constant should be either removed
or applied to the image element in this component (PostCard / the component in
post-card.tsx); choose one: if responsive sizes were intended, add the
sizes={imageSizes} prop to the <img> (or the Image component being rendered) so
the browser/renderer can use it, otherwise delete the imageSizes declaration to
remove dead code and update any related imports or variable references
accordingly.
- Line 44: The wrapper class string in the PostCard component contains an
invalid Tailwind utility ("cover") in the expression vertical && "order-none!
h-52 cover"; remove the "cover" token so the conditional becomes vertical &&
"order-none! h-52" (or replace it with a valid intented utility if you meant a
specific layout effect). Update the string in the component where the
conditional class is used (the expression containing "order-none! h-52 cover")
and ensure object-cover remains on the image element (used earlier) as-is.

---

Nitpick comments:
In `@packages/ui/src/components/post-card.tsx`:
- Line 27: Remove the redundant alias by deleting the const isFeatured =
featured; line and replace all uses of isFeatured in the component with the
original prop/variable featured (e.g., update any conditional checks, classes,
or JSX that reference isFeatured to use featured instead). Ensure
imports/props/destructuring that provide featured remain unchanged and run
tests/typechecks to confirm no remaining references to isFeatured.
- Around line 104-111: The Card usage in the post-card component applies heavy
"!important" style resets (classes like "rounded-none!" and "border-none!") —
change this by either (A) updating the Card API to accept a new variant prop
(e.g., Card variant="flat") and map that variant to the flat styles inside the
Card implementation, then replace the inline "!important" classes on the Card
component in this file with variant="flat" and only the remaining needed layout
classes via className, or (B) if the Card base styles are not needed for this
layout, swap the Card here for a plain semantic container (e.g., a div) and move
the layout classes (cn(..., !vertical && "order-2 gap-0")) to that container;
reference the Card component usage and the local variables postBody and vertical
when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bc9cef23-437b-4488-880f-5889a74b37be

📥 Commits

Reviewing files that changed from the base of the PR and between 6876053 and 13c43c5.

📒 Files selected for processing (3)
  • apps/site/src/app/(prisma-with)/nextjs/page.tsx
  • apps/site/src/data/prisma-with/nextjs.json
  • packages/ui/src/components/post-card.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/site/src/data/prisma-with/nextjs.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/site/src/app/(prisma-with)/nextjs/page.tsx

vertical?: boolean;
}) {
const isFeatured = featured;
const imageSizes = isFeatured ? "(min-width: 640px) 50vw, 100vw" : "384px";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unused variable imageSizes.

The imageSizes variable is computed but never used in the component. If this was intended for the <img> tag's sizes attribute (for responsive images), it's missing. Otherwise, this dead code should be removed.

🔧 Either use it or remove it

Option A: Remove if not needed

-  const imageSizes = isFeatured ? "(min-width: 640px) 50vw, 100vw" : "384px";

Option B: Add to img tag if responsive sizing was intended

         <img
           src={post.imageSrc}
           alt={post.imageAlt ?? post.title}
           className={imageClassName}
           loading={isFeatured ? "eager" : "lazy"}
+          sizes={imageSizes}
         />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const imageSizes = isFeatured ? "(min-width: 640px) 50vw, 100vw" : "384px";
// Line 28 removed - imageSizes variable is deleted
Suggested change
const imageSizes = isFeatured ? "(min-width: 640px) 50vw, 100vw" : "384px";
<img
src={post.imageSrc}
alt={post.imageAlt ?? post.title}
className={imageClassName}
loading={isFeatured ? "eager" : "lazy"}
sizes={imageSizes}
/>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/post-card.tsx` at line 28, The computed but unused
imageSizes constant should be either removed or applied to the image element in
this component (PostCard / the component in post-card.tsx); choose one: if
responsive sizes were intended, add the sizes={imageSizes} prop to the <img> (or
the Image component being rendered) so the browser/renderer can use it,
otherwise delete the imageSizes declaration to remove dead code and update any
related imports or variable references accordingly.

const imageWrapperClassName = cn(
"relative aspect-video w-full h-full",
isFeatured ? "order-1" : "order-2 max-w-96 hidden sm:block",
vertical && "order-none! h-52 cover",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if 'cover' is defined as a custom utility class anywhere in the codebase
rg -n "\.cover\s*\{" --type css
rg -n "'cover'|\"cover\"" --glob "tailwind.config.*"

Repository: prisma/web

Length of output: 202


🏁 Script executed:

# First, let's look at the actual code in post-card.tsx around line 44
cat -n packages/ui/src/components/post-card.tsx | head -60

Repository: prisma/web

Length of output: 2277


🏁 Script executed:

# Search for cover class definition more broadly (without type restrictions)
rg -n "cover\s*\{" --type-list | head -20
rg -in "cover\s*:" packages/ui/src --type css --type scss --type postcss

Repository: prisma/web

Length of output: 472


🏁 Script executed:

# Check tailwind config files
fd -name "tailwind.config.*" -o -name "tailwind.js"

Repository: prisma/web

Length of output: 284


🏁 Script executed:

# Search for any custom utility definitions that might include 'cover'
rg -n "addUtilities|addComponents|addBase" packages/ui --type typescript --type javascript --type jsx

Repository: prisma/web

Length of output: 88


🏁 Script executed:

# Find tailwind config files
find . -name "tailwind.config*" -type f 2>/dev/null | head -20

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Search for 'cover' class definition in CSS/PostCSS files
rg "cover" --type css --type json 2>/dev/null | grep -i "cover\s*[:{]" | head -20

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Broader search for custom utility setup with 'cover'
rg "cover" packages/ui --type json --type css 2>/dev/null | head -30

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Search for any plugin or utility configuration that might define 'cover'
rg "addUtilities|addComponents|extend.*colors|extend.*" --type json packages/ 2>/dev/null | head -40

Repository: prisma/web

Length of output: 115


🏁 Script executed:

# Check root directory
ls -la | grep -i tailwind
cat tailwind.config.* 2>/dev/null | head -50

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Check packages directory structure
ls -la packages/

Repository: prisma/web

Length of output: 269


🏁 Script executed:

# Look for CSS files that might define utilities
find packages/ui/src -name "*.css" -o -name "*.postcss" 2>/dev/null

Repository: prisma/web

Length of output: 88


🏁 Script executed:

# Check the eclipse package for component definitions
find packages -name "eclipse" -type d 2>/dev/null

Repository: prisma/web

Length of output: 70


🏁 Script executed:

# Search for 'cover' in all file types across the entire packages/ui
rg "cover" packages/ui/ 2>/dev/null | head -40

Repository: prisma/web

Length of output: 256


🏁 Script executed:

# Check globals.css for any custom utilities
cat packages/ui/src/styles/globals.css

Repository: prisma/web

Length of output: 4100


🏁 Script executed:

# Look for all CSS files in the project
find . -name "*.css" -type f 2>/dev/null | grep -v node_modules | head -20

Repository: prisma/web

Length of output: 339


🏁 Script executed:

# Check package.json to understand tailwind setup
cat packages/ui/package.json | grep -A 10 -B 5 "tailwind\|@tailwindcss"

Repository: prisma/web

Length of output: 554


🏁 Script executed:

# Search for .cover class definition in all CSS files
rg "\.cover\s*\{" --type css 2>/dev/null

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Check eclipse package for any utility definitions
ls -la packages/eclipse/src/

Repository: prisma/web

Length of output: 495


🏁 Script executed:

# Check if there's a tailwind config in apps or root
find . -maxdepth 3 -name "tailwind.config*" -o -name "postcss.config*" 2>/dev/null | grep -v node_modules

Repository: prisma/web

Length of output: 251


🏁 Script executed:

# Search in eclipse package for any cover definition
rg "cover" packages/eclipse/src/ 2>/dev/null

Repository: prisma/web

Length of output: 148


🏁 Script executed:

# Check if there's any CSS that might be defining cover as a utility
rg "@apply.*cover|\.cover\s*\{|\bcover\b.*@apply" . 2>/dev/null | grep -v node_modules | head -20

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Final check: search all CSS/PostCSS in the entire repo for 'cover'
find . -name "*.css" -o -name "*.postcss" 2>/dev/null | grep -v node_modules | xargs grep -l "cover" 2>/dev/null

Repository: prisma/web

Length of output: 38


🏁 Script executed:

# Check postcss.config to see if there's any custom configuration that might define 'cover'
cat packages/ui/postcss.config.mjs

Repository: prisma/web

Length of output: 192


🏁 Script executed:

# Look for any custom utility definitions in the entire codebase
rg "addUtilities|addComponents" . --type typescript --type javascript 2>/dev/null | grep -v node_modules

Repository: prisma/web

Length of output: 36


🏁 Script executed:

# Search the entire codebase for patterns of 'cover' being used alone (not bg-cover or object-cover)
rg "\bcover\b['\"]" packages/ui packages/eclipse 2>/dev/null

Repository: prisma/web

Length of output: 231


Remove invalid cover utility class.

The cover class is not a standard Tailwind utility and doesn't exist as a custom utility in this codebase. This appears to be a typo on a wrapper <div> element. The object-cover utility used on line 38 is correctly applied to the image itself. Consider removing cover from line 44 unless there's a specific styling intent for the wrapper—in which case, clarify what effect you're trying to achieve with the vertical layout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/post-card.tsx` at line 44, The wrapper class
string in the PostCard component contains an invalid Tailwind utility ("cover")
in the expression vertical && "order-none! h-52 cover"; remove the "cover" token
so the conditional becomes vertical && "order-none! h-52" (or replace it with a
valid intented utility if you meant a specific layout effect). Update the string
in the component where the conditional class is used (the expression containing
"order-none! h-52 cover") and ensure object-cover remains on the image element
(used earlier) as-is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant