Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PPR Experiment #59

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions apps/nextjs/next.config.js → apps/nextjs/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { fileURLToPath } from "url";
import { NextConfig } from "next";
import createJiti from "jiti";

// Import env files to validate at build time. Use jiti so we can load .ts files in here.
createJiti(fileURLToPath(import.meta.url))("./src/env");

/** @type {import("next").NextConfig} */
const config = {
const config: NextConfig = {
reactStrictMode: true,
compiler: {
removeConsole: {
exclude: ["error"],
},
},

experimental: {
ppr: "incremental",
},
Comment on lines +16 to +18
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

PPR configuration needs attention due to inconsistency across apps

The verification reveals that PPR is configured differently across the Next.js applications in the monorepo:

  • apps/nextjs/next.config.ts: uses ppr: "incremental"
  • apps/website/next.config.ts: uses ppr: true
  • Some pages explicitly enable PPR with export const experimental_ppr = true

This inconsistency in PPR configuration could lead to unexpected behavior. Consider:

  • Aligning PPR configuration across all Next.js applications
  • Documenting the rationale for using different PPR modes
  • Testing the applications thoroughly as PPR is still experimental
🔗 Analysis chain

Verify the implications of enabling experimental PPR.

While enabling Partial Page Rendering (PPR) can improve performance, please note:

  1. This is an experimental Next.js feature
  2. Consider documenting the decision and testing strategy
  3. Have a rollback plan in case of issues

Let's check if this aligns with the Next.js version being used:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Next.js version compatibility with PPR feature

# Check package.json for Next.js version
echo "Checking Next.js version:"
cat package.json | grep '"next":'

# Look for any PPR-related configurations across the codebase
echo -e "\nChecking for other PPR configurations:"
rg -l "ppr.*incremental" --type ts

Length of output: 253


Script:

#!/bin/bash
# Let's check the actual Next.js version and PPR documentation references

# Check the exact Next.js version from package-lock.json or yarn.lock
echo "Checking Next.js version from lock files:"
if [ -f "package-lock.json" ]; then
    cat package-lock.json | grep -A 5 '"next":'
elif [ -f "yarn.lock" ]; then
    cat yarn.lock | grep -A 5 "next@"
fi

# Look for any PPR-related documentation or comments
echo -e "\nChecking for PPR documentation or comments:"
rg -i "ppr|partial page rendering" -A 3 -B 3

# Check if there are any dynamic routes or pages that might be affected by PPR
echo -e "\nChecking for dynamic routes:"
fd -e tsx -e ts -e js -e jsx --full-path '.*\[.*\].*' apps/nextjs/

Length of output: 29270

images: {
remotePatterns: [
{
Expand Down
4 changes: 2 additions & 2 deletions apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"build": "pnpm with-env next build",
"clean": "git clean -xdf .next .turbo node_modules",
"dev": "pnpm with-env next dev",
"dev": "NODE_OPTIONS='--inspect' pnpm with-env next dev",
"format": "prettier --check . --ignore-path ../../.gitignore",
"lint": "eslint",
"start": "pnpm with-env next start",
Expand Down Expand Up @@ -35,7 +35,7 @@
"date-fns-tz": "^3.2.0",
"geist": "^1.3.0",
"motion-number": "^0.1.7",
"next": "^14.2.14",
"next": "15.0.2-canary.5",
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Based on the package.json files analysis, I can now provide a final response:

⚠️ Next.js 15 canary version poses compatibility risks

The upgrade to Next.js 15.0.2-canary.5 raises significant concerns:

  1. This version is used in multiple packages within the monorepo:

    • apps/nextjs/package.json
    • apps/website/package.json
  2. The current setup shows tight integration with:

    • React 18.3.1 (pre-release)
    • Multiple tRPC packages (v11.0.0-rc.477)
    • Various UI components and dependencies
  3. The @amaxa/auth package is still using Next.js 14.2.14, which could lead to version conflicts within the monorepo.

Recommendation: Maintain version consistency by either:

  • Keeping all packages on the stable Next.js 14.x series
  • Or if testing Next.js 15 features is necessary, upgrade all dependent packages together in a separate development branch
🔗 Analysis chain

⚠️ Caution: Using Next.js canary release in package.json

The update to "next": "15.0.2-canary.5" raises several concerns:

  1. This is a pre-release (canary) version of Next.js 15, which is not recommended for production use as it may contain unstable features and bugs.

  2. The major version bump from 14 to 15 likely introduces breaking changes that will require migration work.

  3. Using React 18.3.1 (also pre-release) with this specific Next.js canary version may lead to compatibility issues.

Let's verify the official migration guide and current stable version:

Recommendation: Consider using the latest stable version of Next.js 14.x instead of the canary release to ensure stability and production readiness. If you need to test Next.js 15 features, consider doing so in a separate development branch.


Let me try a different approach to gather information about Next.js versions and potential breaking changes.

This will help us:

  1. Understand the full dependency tree and Next.js usage across the project
  2. Check Next.js configurations that might be affected by version 15
  3. Find any internal documentation about upgrades or migrations
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Get the latest stable version of Next.js from npm
echo "Latest stable Next.js version:"
npm show next version

# Check if there's an official blog post or migration guide for Next.js 15
echo -e "\nSearching for Next.js 15 migration guide:"
gh search repos --owner vercel --match name "next.js" --json description | jq '.[] | select(.description | contains("15"))'

Length of output: 388


Script:

#!/bin/bash
# Check package.json files for Next.js related dependencies and configurations
echo "Checking package.json files:"
fd package.json --exec cat {}

# Look for Next.js configuration files
echo -e "\nChecking Next.js config files:"
fd next.config --extension js,mjs,ts,mts --exec cat {}

# Search for any migration or upgrade related documentation in the repository
echo -e "\nSearching for migration/upgrade documentation:"
rg -i "upgrade|migration|breaking.?changes" -g "*.md"

Length of output: 15756

"nuqs": "^1.19.3",
"posthog-js": "^1.167.0",
"react": "18.3.1",
Expand Down
2 changes: 2 additions & 0 deletions apps/nextjs/src/app/(auth)/sign-in/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { Label } from "@amaxa/ui/label";

import { env } from "~/env";

export const experimental_ppr = true;

export default function LoginForm() {
return (
<div className="flex h-screen flex-col items-center justify-center">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,5 @@
"use client";

import { TrendingUp } from "lucide-react";
import {
Area,
AreaChart,
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";

import type { ChartConfig } from "@amaxa/ui/chart";
import {
Card,
CardContent,
Expand All @@ -22,117 +8,87 @@ import {
CardHeader,
CardTitle,
} from "@amaxa/ui/card";

import {
getTaskPriorities,
getTasksOverTime,
getTaskStatuses,
} from "~/server/scripts";
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@amaxa/ui/chart";
TaskPriorityChartClient,
TasksOverTimeChartClient,
TaskStatusChartClient,
} from "./chart-client";

import { api } from "~/trpc/react";
export async function TasksOverTimeChart({
params,
}: {
params: Promise<{ id: string }>;
}): Promise<React.ReactElement> {
const projectId = (await params).id;
const taskData = await getTasksOverTime({ projectId });

const chartConfig = {
tasksFinished: {
label: "Tasks Finished",
color: "hsl(var(--primary))",
},
} satisfies ChartConfig;
return (
<Card className="col-span-2 row-span-1">
<CardHeader>
<CardTitle>Tasks Finished Over Time</CardTitle>
<CardDescription>Number of tasks completed each month</CardDescription>
</CardHeader>
<CardContent>
<TasksOverTimeChartClient taskData={taskData} />
</CardContent>
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none">
Trending up by 10% this month <TrendingUp className="h-4 w-4" />
</div>
<div className="flex items-center gap-2 leading-none text-muted-foreground">
{taskData[0]?.month} - {taskData[taskData.length - 1]?.month}
</div>
</div>
</div>
</CardFooter>
</Card>
);
}

export function ProjectDashboard({ id }: { id: string }) {
const [taskData] = api.tasks.getTasksOverTime.useSuspenseQuery({
projectId: id,
});
const [priorityData] = api.tasks.getTaskPriorities.useSuspenseQuery({
projectId: id,
});
const [statusData] = api.tasks.getTaskStatuses.useSuspenseQuery({
projectId: id,
});
export async function TaskStatusChart({
params,
}: {
params: Promise<{ id: string }>;
}): Promise<React.ReactElement> {
const projectId = (await params).id;
const statusData = await getTaskStatuses({ projectId });

return (
<main className="max-h-screen px-10">
<div className="grid grid-rows-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
<Card className="col-span-2 row-span-1">
<CardHeader>
<CardTitle>Tasks Finished Over Time</CardTitle>
<CardDescription>
Number of tasks completed each month
</CardDescription>
</CardHeader>
<CardContent>
<ChartContainer config={chartConfig} className="h-[300px] w-full">
<AreaChart
data={taskData}
margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<ChartTooltip content={<ChartTooltipContent />} />
<Area
type="monotone"
dataKey="tasksFinished"
stroke={chartConfig.tasksFinished.color}
fill={chartConfig.tasksFinished.color}
/>
</AreaChart>
</ChartContainer>
</CardContent>
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none">
Trending up by 10% this month{" "}
<TrendingUp className="h-4 w-4" />
</div>
<div className="flex items-center gap-2 leading-none text-muted-foreground">
{taskData[0]?.month} - {taskData[taskData.length - 1]?.month}
</div>
</div>
</div>
</CardFooter>
</Card>
<Card className="col-span-1 row-span-1">
<CardHeader>
<CardTitle>Task Status</CardTitle>
</CardHeader>
<CardContent>
<ChartContainer className="h-full w-full" config={chartConfig}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={statusData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="status" />
<YAxis />
<Tooltip content={<ChartTooltipContent />} />
<Bar dataKey="count" fill="#8884d8" />
</BarChart>
</ResponsiveContainer>
</ChartContainer>
</CardContent>
</Card>
<Card className="col-span-3 row-span-1">
<CardHeader>
<CardTitle>Task Priority</CardTitle>
</CardHeader>
<CardContent>
<ChartContainer className="h-full w-full" config={chartConfig}>
<ResponsiveContainer width="100%" height="100%">
<BarChart data={priorityData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="priority" />
<YAxis />
<Tooltip content={<ChartTooltipContent />} />
<Bar dataKey="count" fill="#8884d8" />
</BarChart>
</ResponsiveContainer>
</ChartContainer>
</CardContent>
</Card>
</div>
</main>
<Card className="col-span-1 row-span-1">
<CardHeader>
<CardTitle>Task Status</CardTitle>
</CardHeader>
<CardContent>
<TaskStatusChartClient statusData={statusData} />
</CardContent>
</Card>
);
}

export async function TaskPriorityChart({
params,
}: {
params: Promise<{ id: string }>;
}): Promise<React.ReactElement> {
const projectId = (await params).id;
const priorityData = await getTaskPriorities({ projectId });

return (
<Card className="col-span-3 row-span-1">
<CardHeader>
<CardTitle>Task Priority</CardTitle>
</CardHeader>
<CardContent>
<TaskPriorityChartClient priorityData={priorityData} />
</CardContent>
</Card>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"use client";

import {
Area,
AreaChart,
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
XAxis,
YAxis,
} from "recharts";

import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@amaxa/ui/chart";

const chartConfig = {
tasksFinished: {
label: "Tasks Finished",
color: "hsl(var(--primary))",
},
count: {
label: "Count",
color: "hsl(var(--primary))",
},
};

export function TasksOverTimeChartClient({
taskData,
}: {
taskData: Array<{ month: string; tasksFinished: number }>;
}) {
return (
<ChartContainer config={chartConfig} className="h-[300px] w-full">
<ResponsiveContainer width="100%" height="100%">
<AreaChart
data={taskData}
margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="month" />
<YAxis />
<ChartTooltip content={<ChartTooltipContent />} />
<Area
type="monotone"
dataKey="tasksFinished"
stroke={chartConfig.tasksFinished.color}
fill={chartConfig.tasksFinished.color}
/>
</AreaChart>
</ResponsiveContainer>
</ChartContainer>
);
}

export function TaskStatusChartClient({
statusData,
}: {
statusData: Array<{ status: string; count: number }>;
}) {
return (
<ChartContainer className="h-full w-full" config={chartConfig}>
<ResponsiveContainer width="100%" height={200}>
<BarChart data={statusData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="status" />
<YAxis />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="count" fill={chartConfig.count.color} />
</BarChart>
</ResponsiveContainer>
</ChartContainer>
);
}

export function TaskPriorityChartClient({
priorityData,
}: {
priorityData: Array<{ priority: string; count: number }>;
}) {
return (
<ChartContainer className="h-full w-full" config={chartConfig}>
<ResponsiveContainer width="100%" height={200}>
<BarChart data={priorityData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="priority" />
<YAxis />
<ChartTooltip content={<ChartTooltipContent />} />
<Bar dataKey="count" fill={chartConfig.count.color} />
</BarChart>
</ResponsiveContainer>
</ChartContainer>
);
}
Loading