-
Notifications
You must be signed in to change notification settings - Fork 73
Import the next-partial-prerendering example #308
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
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
/.yarn | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
.pnpm-debug.log* | ||
|
||
# local env files | ||
.env* | ||
!.env*.example | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"singleQuote": true | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
## Next.js Partial Prerendering | ||
|
||
This is a demo of [Next.js](https://nextjs.org) using [Partial Prerendering](https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering). | ||
|
||
This template uses the new Next.js [App Router](https://nextjs.org/docs/app). This includes support for enhanced layouts, colocation of components, tests, and styles, component-level data fetching, and more. | ||
|
||
It also uses the experimental Partial Prerendering feature available in Next.js 14. Partial Prerendering combines ultra-quick static edge delivery with fully dynamic capabilities and we believe it has the potential to [become the default rendering model for web applications](https://vercel.com/blog/partial-prerendering-with-next-js-creating-a-new-default-rendering-model), bringing together the best of static site generation and dynamic delivery. | ||
|
||
> ⚠️ Please note that PPR is an experimental technology that is not recommended for production. You may run into some DX issues, especially on larger code bases. | ||
|
||
## How it works | ||
|
||
The index route `/` uses Partial Prerendering through: | ||
|
||
1. Enabling the experimental flag in `next.config.js`. | ||
|
||
```js | ||
experimental: { | ||
ppr: true, | ||
}, | ||
``` | ||
|
||
2. Using `<Suspense />` to wrap Dynamic content. |
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import { CartCountProvider } from '#/components/cart-count-context'; | ||
import { Header } from '#/components/header'; | ||
import { Sidebar } from '#/components/sidebar'; | ||
import { Metadata } from 'next'; | ||
import { GlobalStyles } from './styles'; | ||
|
||
export const metadata: Metadata = { | ||
metadataBase: new URL('https://partialprerendering.com'), | ||
title: 'Next.js Partial Prerendering', | ||
description: 'A demo of Next.js using Partial Prerendering.', | ||
openGraph: { | ||
title: 'Next.js Partial Prerendering', | ||
description: 'A demo of Next.js using Partial Prerendering.', | ||
}, | ||
twitter: { | ||
card: 'summary_large_image', | ||
}, | ||
}; | ||
|
||
export default function RootLayout({ | ||
children, | ||
}: { | ||
children: React.ReactNode; | ||
}) { | ||
return ( | ||
<html lang="en" className={`[color-scheme:dark]`}> | ||
<head> | ||
<GlobalStyles /> | ||
</head> | ||
<body className="overflow-y-scroll bg-gray-1100 bg-[url('/grid.svg')] pb-36"> | ||
<Sidebar /> | ||
<div className="lg:pl-72"> | ||
<div className="mx-auto max-w-4xl space-y-8 px-2 pt-20 lg:px-8 lg:py-8"> | ||
<div className="rounded-lg bg-vc-border-gradient p-px shadow-lg shadow-black/20"> | ||
<div className="rounded-lg bg-black p-3.5 lg:p-6"> | ||
<CartCountProvider> | ||
<div className="space-y-10"> | ||
<Header /> | ||
|
||
{children} | ||
</div> | ||
</CartCountProvider> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</body> | ||
</html> | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export default function NotFound() { | ||
return ( | ||
<div className="space-y-4 text-vercel-pink"> | ||
<h2 className="text-lg font-bold">Not Found</h2> | ||
<p className="text-sm">Could not find requested resource</p> | ||
</div> | ||
); | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Suspense } from 'react'; | ||
import { | ||
RecommendedProducts, | ||
RecommendedProductsSkeleton, | ||
} from '#/components/recommended-products'; | ||
import { Reviews, ReviewsSkeleton } from '#/components/reviews'; | ||
import { SingleProduct } from '#/components/single-product'; | ||
import { Ping } from '#/components/ping'; | ||
|
||
export default function Page() { | ||
return ( | ||
<div className="space-y-8 lg:space-y-14"> | ||
<SingleProduct /> | ||
|
||
<Ping /> | ||
|
||
<Suspense fallback={<RecommendedProductsSkeleton />}> | ||
<RecommendedProducts /> | ||
</Suspense> | ||
|
||
<Ping /> | ||
|
||
<Suspense fallback={<ReviewsSkeleton />}> | ||
<Reviews /> | ||
</Suspense> | ||
</div> | ||
); | ||
} |
Large diffs are not rendered by default.
Oops, something went wrong.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions
56
examples/next-partial-prerendering/components/add-to-cart.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
'use client'; | ||
|
||
import { useRouter } from 'next/navigation'; | ||
import { useTransition } from 'react'; | ||
import { useCartCount } from '#/components/cart-count-context'; | ||
|
||
export function AddToCart({ initialCartCount }: { initialCartCount: number }) { | ||
const router = useRouter(); | ||
const [isPending, startTransition] = useTransition(); | ||
|
||
const [, setOptimisticCartCount] = useCartCount(initialCartCount); | ||
|
||
const addToCart = () => { | ||
setOptimisticCartCount(initialCartCount + 1); | ||
|
||
// update the cart count cookie | ||
document.cookie = `_cart_count=${initialCartCount + 1}; path=/; max-age=${ | ||
60 * 60 * 24 * 30 | ||
}};`; | ||
|
||
// Normally you would also send a request to the server to add the item | ||
// to the current users cart | ||
// await fetch(`https://api.acme.com/...`); | ||
|
||
// Use a transition and isPending to create inline loading UI | ||
startTransition(() => { | ||
setOptimisticCartCount(null); | ||
|
||
// Refresh the current route and fetch new data from the server without | ||
// losing client-side browser or React state. | ||
router.refresh(); | ||
|
||
// We're working on more fine-grained data mutation and revalidation: | ||
// https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions | ||
}); | ||
}; | ||
|
||
return ( | ||
<button | ||
className="relative w-full items-center space-x-2 rounded-lg bg-vercel-blue px-3 py-1 text-sm font-medium text-white hover:bg-vercel-blue/90 disabled:text-white/70" | ||
onClick={addToCart} | ||
disabled={isPending} | ||
> | ||
Add to Cart | ||
{isPending ? ( | ||
<div className="absolute right-2 top-1.5" role="status"> | ||
<div | ||
className=" | ||
h-4 w-4 animate-spin rounded-full border-[3px] border-white border-r-transparent" | ||
/> | ||
<span className="sr-only">Loading...</span> | ||
</div> | ||
) : null} | ||
</button> | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { VercelLogo } from '#/components/vercel-logo'; | ||
|
||
export default function Byline({ className }: { className: string }) { | ||
return ( | ||
<div | ||
className={`${className} inset-x-0 bottom-3 mx-3 rounded-lg bg-vc-border-gradient p-px shadow-lg shadow-black/20`} | ||
> | ||
<div className="flex flex-row justify-between rounded-lg bg-black p-3.5 lg:px-5 lg:py-3"> | ||
<div className="flex items-center gap-x-1.5"> | ||
<div className="text-sm text-gray-400">By</div> | ||
<a href="https://vercel.com" title="Vercel"> | ||
<div className="w-16 text-gray-100 hover:text-gray-50"> | ||
<VercelLogo /> | ||
</div> | ||
</a> | ||
</div> | ||
|
||
<div className="text-sm text-gray-400"> | ||
<a | ||
className="underline decoration-dotted underline-offset-4 transition-colors hover:text-gray-300" | ||
href="https://github.com/vercel-labs/next-partial-prerendering" | ||
target="_blank" | ||
rel="noreferrer" | ||
> | ||
View code | ||
</a> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} |
35 changes: 35 additions & 0 deletions
35
examples/next-partial-prerendering/components/cart-count-context.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
'use client'; | ||
|
||
import React, { useState } from 'react'; | ||
|
||
const CartCountContext = React.createContext< | ||
| [null | number, React.Dispatch<React.SetStateAction<null | number>>] | ||
| undefined | ||
>(undefined); | ||
|
||
export function CartCountProvider({ children }: { children: React.ReactNode }) { | ||
const [optimisticCartCount, setOptimisticCartCount] = useState<null | number>( | ||
null, | ||
); | ||
|
||
return ( | ||
<CartCountContext.Provider | ||
value={[optimisticCartCount, setOptimisticCartCount]} | ||
> | ||
{children} | ||
</CartCountContext.Provider> | ||
); | ||
} | ||
|
||
export function useCartCount( | ||
initialCount: number, | ||
): [null | number, React.Dispatch<React.SetStateAction<null | number>>] { | ||
const context = React.useContext(CartCountContext); | ||
if (context === undefined) { | ||
throw new Error('useCartCount must be used within a CartCountProvider'); | ||
} | ||
if (context[0] === null) { | ||
return [initialCount, context[1]]; | ||
} | ||
return context; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
'use client'; | ||
|
||
import { useCartCount } from '#/components/cart-count-context'; | ||
|
||
export function CartCount({ initialCartCount }: { initialCartCount: number }) { | ||
const [count] = useCartCount(initialCartCount); | ||
return <span>{count}</span>; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { NextLogo } from '#/components/next-logo'; | ||
import { | ||
MagnifyingGlassIcon, | ||
ShoppingCartIcon, | ||
} from '@heroicons/react/24/solid'; | ||
import Image from 'next/image'; | ||
import { CartCount } from '#/components/cart-count'; | ||
import { cookies } from 'next/headers'; | ||
import { Suspense } from 'react'; | ||
|
||
async function CartCountFromCookies() { | ||
const cartCount = Number(cookies().get('_cart_count')?.value || '0'); | ||
return <CartCount initialCartCount={cartCount} />; | ||
} | ||
|
||
export function Header() { | ||
return ( | ||
<div className="flex items-center justify-between gap-x-3 rounded-lg bg-gray-800 px-3 py-3 lg:px-5 lg:py-4"> | ||
<div className="flex gap-x-3"> | ||
<div className="h-10 w-10 hover:opacity-70"> | ||
<NextLogo /> | ||
</div> | ||
|
||
<div className="relative flex-1"> | ||
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3"> | ||
<MagnifyingGlassIcon className="h-5 w-5 text-gray-300" /> | ||
</div> | ||
<input | ||
aria-label="Search" | ||
type="search" | ||
name="search" | ||
id="search" | ||
className="block w-full rounded-full border-none bg-gray-600 pl-10 font-medium text-gray-200 focus:border-vercel-pink focus:ring-2 focus:ring-vercel-pink" | ||
autoComplete="off" | ||
/> | ||
</div> | ||
</div> | ||
|
||
<div className="flex shrink-0 gap-x-3"> | ||
<div className="relative flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-gray-600 text-white"> | ||
<ShoppingCartIcon className="w-6 text-white" /> | ||
<div className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-vercel-cyan text-sm font-bold text-cyan-800"> | ||
<Suspense fallback={<span></span>}> | ||
<CartCountFromCookies /> | ||
</Suspense> | ||
</div> | ||
</div> | ||
|
||
<Image | ||
src="/prince-akachi-LWkFHEGpleE-unsplash.jpg" | ||
className="rounded-full" | ||
width={40} | ||
height={40} | ||
alt="User" | ||
priority | ||
/> | ||
</div> | ||
</div> | ||
); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.