सामग्री पर जाएँ

Neobrutalism को Payload CMS के साथ इंटीग्रेट करें

UI लाइब्रेरीज़ आपके डेवलपमेंट को काफ़ी तेज़ कर सकती हैं। इस लेख में हम आपके अगले React प्रोजेक्ट के लिए टॉप 5 पर नज़र डालेंगे।

एक व्यावहारिक, आद्योपांत गाइड — 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 ऐप बनाएँ

pnpm create payload-app
# Follow the prompts: choose a template (blank or blog), set DB (SQLite for dev), etc.
cd <your-payload-app>
pnpm dev  # or npm run dev / yarn dev
npx create-payload-app
# Follow the prompts: choose a template (blank or blog), set DB (SQLite for dev), etc.
cd <your-payload-app>
pnpm dev  # or npm run dev / yarn dev
yarn create payload-app
# Follow the prompts: choose a template (blank or blog), set DB (SQLite for dev), etc.
cd <your-payload-app>
pnpm dev  # or npm run dev / yarn dev
bunx --bun create-payload-app
# Follow the prompts: choose a template (blank or blog), set DB (SQLite for dev), etc.
cd <your-payload-app>
pnpm dev  # or npm run dev / yarn dev

इससे 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 खोलिए → PostsCreate 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 ऐप बनाएँ

pnpm create next-app@latest retroui-payload-blog
cd retroui-payload-blog
npx create-next-app@latest retroui-payload-blog
cd retroui-payload-blog
yarn create next-app@latest retroui-payload-blog
cd retroui-payload-blog
bunx --bun create-next-app@latest retroui-payload-blog
cd retroui-payload-blog

2) TailwindCSS इंस्टॉल करें (अगर आपने Tailwind टेम्प्लेट नहीं चुना)

pnpm i -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

tailwind.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.js Payload डोमेन को images.domains के तहत अनुमति देता है।
  • कुछ भी रेंडर नहीं होता: lib/payload.ts में fetch URL की जाँच कीजिए। सुनिश्चित कीजिए कि रिकॉर्ड मौजूद हैं और status published है।

सारांश

अब आपके पास है:

  • एक Payload CMS ऐप जो REST पर पोस्ट उजागर करती है
  • एक Next.js साइट जो Neobrutalism से स्टाइल की गई है
  • एक /blog जो अलग-अलग पोस्ट सूचीबद्ध करता है और उनसे लिंक करता है

यहाँ से, ऑथर, कैटेगरी और tags जोड़िए; और Neobrutalism कंपोनेंट के साथ स्टाइल कीजिए; और उसे शिप कीजिए।


← ब्लॉग पर वापस