11import { StripePanels } from "@/components/stripe-panels" ;
2+ import { DemoTabs } from "@/components/demo-tabs" ;
23import { ComponentPreview } from "@/components/component-preview" ;
34import Image from "next/image" ;
5+ import { getPlaceholder } from "next-image-placeholder" ;
6+ import { codeToHtml } from "shiki" ;
7+
8+ const DEMO_IMAGE = "https://images.unsplash.com/photo-1619441207978-3d326c46e2c9?q=80&w=2669&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" ;
9+
10+ export default async function Home ( ) {
11+ const initialData = await getPlaceholder ( DEMO_IMAGE ) ;
12+
13+ const blurCode = `// Server Component
14+ const { base64 } = await getPlaceholder(
15+ "${ DEMO_IMAGE } "
16+ );
17+
18+ // Render
19+ <Image
20+ src="${ DEMO_IMAGE } "
21+ placeholder="blur"
22+ blurDataURL={base64}
23+ alt="Demo"
24+ />` ;
25+
26+ const colorCode = `// Server Component
27+ const color = await getColor(
28+ "${ DEMO_IMAGE } "
29+ );
30+
31+ // Render
32+ <div style={{ backgroundColor: color }}>
33+ <Image src="..." />
34+ </div>` ;
35+
36+ const clientCode = `// 1. Create Server Action (app/actions.ts)
37+ 'use server'
38+ import { getPlaceholder } from 'next-image-placeholder'
39+
40+ export async function getPlaceholderAction(imageUrl: string) {
41+ return getPlaceholder(imageUrl)
42+ }
43+
44+ // 2. Use the component (Recommended)
45+ 'use client'
46+ import { PlaceholderImage } from 'next-image-placeholder/react'
47+ import { getPlaceholderAction } from './actions'
48+
49+ export function Gallery({ src }: { src: string }) {
50+ return (
51+ <PlaceholderImage
52+ src={src}
53+ action={getPlaceholderAction}
54+ width={400}
55+ height={300}
56+ alt="Gallery Image"
57+ className="rounded-lg"
58+ />
59+ )
60+ }` ;
61+
62+ const codeSnippets = {
63+ blur : {
64+ code : blurCode ,
65+ html : await codeToHtml ( blurCode , { lang : 'tsx' , theme : 'github-dark-dimmed' } )
66+ } ,
67+ color : {
68+ code : colorCode ,
69+ html : await codeToHtml ( colorCode , { lang : 'tsx' , theme : 'github-dark-dimmed' } )
70+ } ,
71+ client : {
72+ code : clientCode ,
73+ html : await codeToHtml ( clientCode , { lang : 'tsx' , theme : 'github-dark-dimmed' } )
74+ }
75+ } ;
476
5- export default function Home ( ) {
677 return (
778 < div className = "relative min-h-screen flex flex-col items-center overflow-x-hidden" >
879 < StripePanels />
@@ -12,16 +83,16 @@ export default function Home() {
1283 < div className = "h-px w-full bg-neutral-200 dark:bg-neutral-800" />
1384
1485 { /* Hero Section */ }
15- < div className = "flex flex-col items-center justify-center pt-32 pb-24 px-6 border-b border-neutral-200 dark:border-neutral-800 " >
16- < div className = "space-y-8 text-center max-w-4xl" >
86+ < div className = "flex flex-col items-center justify-center pt-20 pb-0 px-6" >
87+ < div className = "space-y-6 text-center max-w-4xl" >
1788 < div className = "inline-flex items-center px-3 py-1 rounded-full border border-neutral-200 dark:border-neutral-800 bg-neutral-50 dark:bg-neutral-900 text-xs font-medium text-neutral-600 dark:text-neutral-400 mb-4" >
1889 v1.1.2 is now available
1990 </ div >
2091
2192 < h1 className = "text-4xl sm:text-5xl md:text-7xl font-bold tracking-tight text-neutral-900 dark:text-neutral-50" >
22- Image Placeholders, < br />
93+ next image < br />
2394 < span className = "text-transparent bg-clip-text bg-gradient-to-r from-neutral-500 to-neutral-800 dark:from-neutral-400 dark:to-neutral-100" >
24- Perfected.
95+ placeholder
2596 </ span >
2697 </ h1 >
2798
@@ -30,7 +101,7 @@ export default function Home() {
30101 Zero client-side JavaScript for generation.
31102 </ p >
32103
33- < div className = "flex flex-col sm:flex-row gap-4 justify-center pt-8 " >
104+ < div className = "flex flex-col sm:flex-row gap-4 justify-center pt-6 " >
34105 < a
35106 href = "/docs"
36107 className = "px-6 py-3 rounded-full bg-neutral-900 dark:bg-neutral-50 text-neutral-50 dark:text-neutral-900 font-medium text-sm hover:opacity-90 transition-opacity text-center"
@@ -49,74 +120,15 @@ export default function Home() {
49120 </ div >
50121 </ div >
51122
52- { /* Feature Grid */ }
53- < div className = "grid grid-cols-1 md:grid-cols-3 divide-y md:divide-y-0 md:divide-x divide-neutral-200 dark:divide-neutral-800 border-b border-neutral-200 dark:border-neutral-800" >
54- { /* Card 1 */ }
55- < div className = "p-12 relative group" >
56- < div className = "absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-indigo-500 to-purple-500 opacity-0 group-hover:opacity-100 transition-opacity" />
57- < h3 className = "text-lg font-semibold mb-3 text-neutral-900 dark:text-neutral-50" > Server-Side Optimisation</ h3 >
58- < p className = "text-neutral-500 dark:text-neutral-400 text-sm leading-relaxed" >
59- Generate placeholders at build time or request time on the server. No client-side waterfall.
60- </ p >
123+ { /* Live Demo Section - Show, Don't Tell */ }
124+ < div className = "divide-y divide-neutral-200 dark:divide-neutral-800 border-b border-neutral-200 dark:border-neutral-800" >
125+ < div className = "px-8 pb-12 pt-12 lg:px-12 lg:pb-12 lg:pt-12" >
126+ < DemoTabs
127+ initialData = { initialData }
128+ sourceUrl = { DEMO_IMAGE }
129+ codeSnippets = { codeSnippets }
130+ />
61131 </ div >
62-
63- { /* Card 2 */ }
64- < div className = "p-12 relative group" >
65- < div className = "absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500 to-cyan-500 opacity-0 group-hover:opacity-100 transition-opacity" />
66- < h3 className = "text-lg font-semibold mb-3 text-neutral-900 dark:text-neutral-50" > Dominant Color</ h3 >
67- < p className = "text-neutral-500 dark:text-neutral-400 text-sm leading-relaxed" >
68- Automatically extract legitimate dominant colors from images for a smooth loading experience.
69- </ p >
70- </ div >
71-
72- { /* Card 3 */ }
73- < div className = "p-12 relative group" >
74- < div className = "absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-emerald-500 to-green-500 opacity-0 group-hover:opacity-100 transition-opacity" />
75- < h3 className = "text-lg font-semibold mb-3 text-neutral-900 dark:text-neutral-50" > Typesafe & Lightweight </ h3 >
76- < p className = "text-neutral-500 dark:text-neutral-400 text-sm leading-relaxed" >
77- Built with TypeScript. Minimal dependencies. Designed for modern React Server Components.
78- </ p >
79- </ div >
80- </ div >
81-
82- { /* Demo Section */ }
83- < div className = "p-12 border-b border-neutral-200 dark:border-neutral-800" >
84- < div className = "text-center mb-16" >
85- < h2 className = "text-3xl font-bold mb-4 text-neutral-900 dark:text-neutral-50" > See it in action</ h2 >
86- < p className = "text-neutral-500 dark:text-neutral-400 max-w-xl mx-auto" >
87- The library generates blur data URLs that you can pass directly to the Next.js Image component.
88- </ p >
89- </ div >
90-
91- < ComponentPreview
92- code = { `import Image from "next/image";
93- import { getPlaceholder } from "next-image-placeholder";
94-
95- export default async function Page() {
96- const { blurDataURL } = await getPlaceholder(
97- "https://images.unsplash.com/photo-1579546929518-9e396f3cc809"
98- );
99-
100- return (
101- <Image
102- src="https://images.unsplash.com/photo-1579546929518-9e396f3cc809"
103- placeholder="blur"
104- blurDataURL={blurDataURL}
105- width={800}
106- height={600}
107- alt="Gradient"
108- />
109- );
110- }` }
111- usage = "npm install next-image-placeholder fast-average-color-node sharp"
112- >
113- < div className = "relative w-full max-w-md aspect-[4/3] rounded-lg overflow-hidden shadow-fancy" >
114- { /* Mockup for preview since we can't run server actions in client component easily without setup */ }
115- < div className = "w-full h-full bg-neutral-200 animate-pulse flex items-center justify-center text-neutral-400 text-sm" >
116- Image Preview
117- </ div >
118- </ div >
119- </ ComponentPreview >
120132 </ div >
121133
122134 </ main >
0 commit comments