14 नवंबर 2025
Neobrutalism को Payload CMS के साथ इंटीग्रेट करें
UI लाइब्रेरीज़ आपके डेवलपमेंट को काफ़ी तेज़ कर सकती हैं। इस लेख में हम आपके अगले React प्रोजेक्ट के लिए टॉप 5 पर नज़र डालेंगे।
Dov Azencot
@DovAzencotएक व्यावहारिक, आद्योपांत गाइड — Payload CMS को अपने कंटेंट बैकएंड के रूप में और Neobrutalism को अपने फ़्रंट-एंड कंपोनेंट किट के रूप में जोड़ने की, और फिर Next.js में एक साफ़-सुथरी, नियोब्रूटलिस्ट ब्लॉग लिस्ट शिप करने की।
बढ़िया चलता है अगर आपका स्टैक Next.js + TailwindCSS है। Payload Next-नेटिव है और Neobrutalism Tailwind-पहले है, इसलिए दोनों बख़ूबी आपस में जुड़ जाते हैं।
आप क्या बनाएँगे
- एक Payload CMS ऐप, जिसमें एक Posts कलेक्शन हो
- एक Next.js फ़्रंट-एंड जो Payload के REST API से पोस्ट फ़ेच करता है
- एक
/blogपेज जो Neobrutalism कंपोनेंट (Cards, Badges, Buttons) का इस्तेमाल करते हुए एक रिस्पॉन्सिव लिस्ट रेंडर करता है
पूर्वापेक्षाएँ
- Node.js 20.9+ (Payload को Node 20.9 या उससे नया चाहिए)
- फ़्रंट-एंड के लिए Next.js 15+ (Payload Next-नेटिव है)
- कोई भी कम्पैटिबल DB: SQLite (dev), Postgres, या MongoDB
सुझाव: लोकल डेवलपमेंट के लिए, SQLite सबसे तेज़ रास्ता है। डिप्लॉय करते समय Postgres/Mongo पर स्विच कीजिए।
भाग 1 — Payload CMS सेट अप करें
आप Payload को किसी मौजूदा Next.js ऐप में जोड़ सकते हैं या एक नई ऐप स्कैफ़ोल्ड कर सकते हैं। हम एक समर्पित Payload प्रोजेक्ट स्कैफ़ोल्ड करेंगे ताकि इसका API साफ़ और पोर्टेबल बना रहे।
1) एक नई Payload ऐप बनाएँ
इससे Payload और उसका एडमिन UI शुरू हो जाता है (आम तौर पर http://localhost:3000/admin पर)।
2) एक Posts कलेक्शन परिभाषित करें
src/collections/Posts.ts बनाएँ:
import { CollectionConfig } from 'payload/types';
const Posts: CollectionConfig = {
slug: 'posts',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'publishedAt', 'status'],
},
access: {
read: () => true, // public read access for blog
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'slug',
type: 'text',
required: true,
unique: true,
},
{
name: 'excerpt',
type: 'textarea',
},
{
name: 'coverImage',
type: 'upload',
relationTo: 'media',
},
{
name: 'status',
type: 'select',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
defaultValue: 'draft',
required: true,
},
{
name: 'publishedAt',
type: 'date',
admin: { position: 'sidebar' },
},
{
name: 'content',
type: 'richText',
},
],
};
export default Posts;इसे अपने मुख्य कॉन्फ़िग src/payload.config.ts में जोड़ें:
import { buildConfig } from 'payload/config';
import Posts from './collections/Posts';
export default buildConfig({
serverURL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
admin: { user: 'users' },
collections: [
Posts,
// Media and Users collections if you need them
],
});अगर आपके पास अभी तक कोई Media या Users कलेक्शन नहीं है, तो
create-payload-appको किसी blog टेम्प्लेट के साथ चलाइए या बाद में अपलोड और ऑथ के लिए साधारण कलेक्शन जोड़िए।
3) कुछ पोस्ट सीड करें
एडमिन UI खोलिए → Posts → Create New और बीते हुए publishedAt तारीख़ों के साथ कुछ पब्लिश की गई पोस्ट जोड़िए।
4) पुष्टि करें कि REST API काम करता है
Payload डिफ़ॉल्ट रूप से /api/<collection> पर एक REST API उजागर करता है।
यहाँ जाइए:
http://localhost:3000/api/posts?limit=10&sort=-publishedAt&where[status][equals]=published
आपको अपनी पोस्ट के साथ JSON दिखना चाहिए।
जब आप डिप्लॉय करें, तो
PAYLOAD_PUBLIC_SERVER_URLऔर अपने DB env वेरिएबल सेट कीजिए। अगर आप Payload को Next.js से अलग होस्ट कर रहे हैं, तो अपने फ़्रंट-एंड ऑरिजिन के लिए CORS सक्षम कीजिए।
भाग 2 — Next.js + Neobrutalism फ़्रंट-एंड सेट अप करें
हम एक Next.js ऐप बनाएँगे जो Payload REST API का उपभोग करती है और Neobrutalism कंपोनेंट का इस्तेमाल करते हुए एक ब्लॉग लिस्ट रेंडर करती है।
1) Next.js ऐप बनाएँ
2) TailwindCSS इंस्टॉल करें (अगर आपने Tailwind टेम्प्लेट नहीं चुना)
pnpm i -D tailwindcss postcss autoprefixer
npx tailwindcss init -ptailwind.config.ts के content में ./components/**/*.{ts,tsx} और ./app/**/*.{ts,tsx} जोड़िए। अगर इंस्टॉलर को ज़रूरत हो तो Neobrutalism के पाथ भी शामिल कीजिए।
3) Neobrutalism इंस्टॉल करें
आधिकारिक इंस्टॉलर (CLI) या मैनुअल इंस्टॉल इस्तेमाल कीजिए। उदाहरण (pnpm):
pnpm add retrouiफिर जहाँ ज़रूरत हो वहाँ कंपोनेंट इम्पोर्ट कीजिए। (अगर Neobrutalism कंपोनेंट कॉपी करने के लिए कोई CLI देती है, तो उसे यहाँ चलाइए और इंडिकेशन का पालन कीजिए।)
4) Payload से कनेक्ट करें (env + fetch हेल्पर)
.env.local बनाएँ:
NEXT_PUBLIC_PAYLOAD_BASE_URL=http://localhost:3000
एक छोटी fetch यूटिलिटी lib/payload.ts जोड़ें:
export type Post = {
id: string;
title: string;
slug: string;
excerpt?: string;
coverImage?: { url: string; filename: string } | string | null;
status: 'draft' | 'published';
publishedAt?: string | null;
};
export async function getPublishedPosts(limit = 12) {
const base = process.env.NEXT_PUBLIC_PAYLOAD_BASE_URL;
const url = new URL('/api/posts', base);
url.searchParams.set('limit', String(limit));
url.searchParams.set('sort', '-publishedAt');
url.searchParams.set('where[status][equals]', 'published');
const res = await fetch(url.toString(), { next: { revalidate: 60 } });
if (!res.ok) throw new Error(`Failed to fetch posts: ${res.status}`);
const data = await res.json();
return data.docs as Post[];
}5) Neobrutalism के साथ ब्लॉग लिस्ट पेज बनाएँ
app/blog/page.tsx बनाएँ (App Router):
import Image from 'next/image';
import Link from 'next/link';
import { getPublishedPosts, type Post } from '@/lib/payload';
// Example Neobrutalism components — adjust paths/names per your install
import { Card } from 'neobrutalism/card';
import { Button } from 'neobrutalism/button';
import { Badge } from 'neobrutalism/badge';
export const revalidate = 60; // ISR cadence
export default async function BlogListPage() {
const posts = await getPublishedPosts(12);
return (
<main className="container mx-auto px-4 py-10">
<h1 className="text-4xl font-extrabold mb-6">Blog</h1>
<p className="text-muted-foreground mb-10">
Latest posts from the Payload CMS backend, styled with Neobrutalism.
</p>
<section className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{posts.map((post) => (
<Card key={post.id} className="p-0 overflow-hidden">
{typeof post.coverImage === 'object' && post.coverImage?.url ? (
<div className="relative h-44 w-full">
<Image
src={post.coverImage.url}
alt={post.title}
fill
className="object-cover"
/>
</div>
) : null}
<div className="p-5 space-y-3">
<div className="flex items-center gap-2">
<Badge variant="outline">Article</Badge>
{post.publishedAt ? (
<span className="text-xs opacity-70">
{new Date(post.publishedAt).toLocaleDateString()}
</span>
) : null}
</div>
<h2 className="text-2xl font-bold leading-tight">
<Link href={`/blog/${post.slug}`}>{post.title}</Link>
</h2>
{post.excerpt ? (
<p className="text-sm text-muted-foreground line-clamp-3">
{post.excerpt}
</p>
) : null}
<div className="pt-2">
<Button asChild>
<Link href={`/blog/${post.slug}`}>Read more</Link>
</Button>
</div>
</div>
</Card>
))}
</section>
</main>
);
}अगर आपका Neobrutalism पैकेज अलग इम्पोर्ट नाम/पाथ उजागर करता है, तो मेल के लिए
import { Card } from 'neobrutalism/card'लाइनों को समायोजित कीजिए। आप किसी भी दूसरे Neobrutalism कंपोनेंट (Tabs, Inputs, आदि) से भी बदल सकते हैं।
6) पोस्ट डिटेल पेज (वैकल्पिक)
app/blog/[slug]/page.tsx बनाएँ:
import Image from 'next/image';
import Link from 'next/link';
async function getPost(slug: string) {
const base = process.env.NEXT_PUBLIC_PAYLOAD_BASE_URL!;
const url = new URL('/api/posts', base);
url.searchParams.set('limit', '1');
url.searchParams.set('where[slug][equals]', slug);
const res = await fetch(url.toString(), { next: { revalidate: 60 } });
if (!res.ok) throw new Error('Failed to load post');
const data = await res.json();
return data.docs[0];
}
export default async function PostPage({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug);
if (!post) return <div className="p-10">Not found</div>;
return (
<article className="container mx-auto px-4 py-10 max-w-3xl">
<Link href="/blog" className="underline">← Back to blog</Link>
<h1 className="text-4xl font-extrabold mt-3">{post.title}</h1>
{post.coverImage?.url ? (
<div className="relative h-80 w-full my-6">
<Image src={post.coverImage.url} alt={post.title} fill className="object-cover" />
</div>
) : null}
{post.content?.root ? (
// If using Payload Lexical richText renderer, render here
<div className="prose prose-neutral dark:prose-invert">
{/* Render your rich text */}
</div>
) : (
post.excerpt ? <p className="mt-4 text-lg">{post.excerpt}</p> : null
)}
</article>
);
}रिच टेक्स्ट के लिए, अपनी पसंद का Payload Renderer इस्तेमाल कीजिए (उदाहरण के लिए, @payloadcms/richtext-lexical) और उसी हिसाब से रेंडर कीजिए।
भाग 3 — Cross-origin और डिप्लॉयमेंट नोट्स
अगर आपकी Next.js ऐप Payload से किसी अलग ऑरिजिन पर चलती है:
- Payload में CORS सक्षम कीजिए:
payload.config.tsमेंcors: ["https://your-next-app.com", "http://localhost:3000"]सेट कीजिए। serverURL(औरPAYLOAD_PUBLIC_SERVER_URL) को सही सेट कीजिए ताकि इमेज URL रिज़ॉल्व हों।- ड्राफ़्ट सुरक्षित कीजिए: ऊपर हमने सार्वजनिक
readकी अनुमति दी थी। निजी कंटेंट के लिए, इसे एकaccess.readफ़ंक्शन से बदलिए जो ऑथ/रोल जाँचे।
डिप्लॉयमेंट विकल्प:
- सेल्फ-होस्ट (किसी VPS पर Node ऐप) या आधुनिक DX के लिए Vercel/Cloudflare पर डिप्लॉय कीजिए।
- झटपट डेमो के लिए SQLite इस्तेमाल कीजिए, prod के लिए Postgres/Mongo।
भाग 4 — सुधार
- सर्च और फ़िल्टर: Payload के क्वेरी पैरामीटर (उदाहरण के लिए,
where[title][like]) इस्तेमाल कीजिए और फ़िल्टर करने के लिए एक Neobrutalism Input + Tabs जोड़िए। - पेजिनेशन: REST API
totalDocs,limit,pageलौटाता है — एक Neobrutalism Button के साथ "Load more" बनाइए। - इमेज:
coverImageको एक समर्पित Media कलेक्शन में ले जाइए और Payload का अपलोड अडैप्टर इस्तेमाल कीजिए। - प्रीव्यू मोड: Next.js में एक ड्राफ़्ट प्रीव्यू रूट सेट कीजिए जो ड्राफ़्ट-ऑथ हेडर के साथ फ़ेच करे।
- GraphQL: Payload GraphQL भी उजागर करता है — अगर आप टाइप की गई क्वेरी पसंद करते हैं तो इसे इस्तेमाल कीजिए।
समस्या-समाधान
- CORS एरर:
corsकॉन्फ़िग जाँचिए और सुनिश्चित कीजिए किserverURLसेट है। - इमेज नहीं दिख रहीं: अपलोड के लिए लौटाया गया
urlपुष्ट कीजिए और यह भी कि Next.js काnext.config.jsPayload डोमेन कोimages.domainsके तहत अनुमति देता है। - कुछ भी रेंडर नहीं होता:
lib/payload.tsमें fetch URL की जाँच कीजिए। सुनिश्चित कीजिए कि रिकॉर्ड मौजूद हैं औरstatuspublishedहै।
सारांश
अब आपके पास है:
- एक Payload CMS ऐप जो REST पर पोस्ट उजागर करती है
- एक Next.js साइट जो Neobrutalism से स्टाइल की गई है
- एक
/blogजो अलग-अलग पोस्ट सूचीबद्ध करता है और उनसे लिंक करता है
यहाँ से, ऑथर, कैटेगरी और tags जोड़िए; और Neobrutalism कंपोनेंट के साथ स्टाइल कीजिए; और उसे शिप कीजिए।