Free tutorials & notes in Hindi & English · Clean code examples 🧰 मुफ़्त PDF और फ़ोटो टूल्स →
🏆 Project – पूरी Website बनाएँ  ·  Lesson 34

Project: Markdown वाला Blog

Markdown blog क्यों?

Database CMS

  • MySQL + admin panel चाहिए
  • हर page DB query करता है
  • Secure करने को ज़्यादा चीज़ें

Markdown files

  • किसी भी editor में posts लिखें
  • Pages एक बार build (SSG) – बहुत तेज़
  • Git में version history
Institute blog के लिए perfect

Step 1: Post लिखिए

content/posts/ की हर file एक post है। --- lines के बीच का हिस्सा front matter (metadata) है।

Markdown – content/posts/how-to-start-coding.md
---
title: How to Start Coding in 2026 – A Simple Roadmap
date: '2026-09-02'
excerpt: Confused where to begin? Follow this 4-step roadmap that our students use.
tags: [roadmap, beginners]
---

Starting to code feels hard only because there are too many choices. Here is the path our students follow.

## 1. Pick one language

Choose **C** if you are in school or B.Tech, or **Python** if you want AI and data science. Do not learn both at the same time.

## 2. Practise every day

Write at least **one small program daily** — even 20 minutes is enough. Typing code yourself is what builds memory.

## 3. Build a tiny project

After 4 weeks, build something small: a calculator, a marks report or a to-do list.

## 4. Learn the web

Once basics are clear, learn HTML, CSS and JavaScript, then a framework like **Next.js**.

> Consistency beats talent. Code a little every day.

Step 2: lib/posts.js

JS – lib/posts.js
import fs from 'node:fs'
import path from 'node:path'
import matter from 'gray-matter'
import { marked } from 'marked'

// Every .md file inside content/posts becomes a blog post.
const POSTS_DIR = path.join(process.cwd(), 'content', 'posts')

export function getAllPosts() {
  return fs
    .readdirSync(POSTS_DIR)
    .filter((f) => f.endsWith('.md'))
    .map((file) => {
      const slug = file.replace(/\.md$/, '')
      const { data } = matter(fs.readFileSync(path.join(POSTS_DIR, file), 'utf8'))
      return { slug, title: data.title, date: data.date, excerpt: data.excerpt, tags: data.tags || [] }
    })
    .sort((a, b) => (a.date < b.date ? 1 : -1)) // newest first
}

export function getPost(slug) {
  const file = path.join(POSTS_DIR, slug + '.md')
  if (!/^[a-z0-9-]+$/.test(slug) || !fs.existsSync(file)) return null
  const { data, content } = matter(fs.readFileSync(file, 'utf8'))
  return { slug, ...data, html: marked.parse(content) }
}

Regex /^[a-z0-9-]+$/ ../../etc/passwd जैसे paths रोकता है — file path बनाने में use होने वाली हर चीज़ validate करें।

Step 3: Blog list page

JSX – app/blog/page.jsx
import PostCard from '@/components/PostCard'
import { getAllPosts } from '@/lib/posts'

export const metadata = {
  title: 'Blog – Coding Tips & Roadmaps',
  description: 'Simple articles on learning to code: roadmaps, language comparisons and project ideas.',
  alternates: { canonical: '/blog' },
}

export default function BlogPage() {
  const posts = getAllPosts()
  return (
    <div className="container section">
      <h1>Blog</h1>
      <p className="lead">{posts.length} articles to help you learn faster.</p>
      <div className="grid" style={{ marginTop: 24 }}>
        {posts.map((p) => (
          <PostCard key={p.slug} post={p} />
        ))}
      </div>
    </div>
  )
}

Step 4: Post page

JSX – app/blog/[slug]/page.jsx
import Link from 'next/link'
import { notFound } from 'next/navigation'
import JsonLd from '@/components/JsonLd'
import { getAllPosts, getPost } from '@/lib/posts'
import { site } from '@/lib/site'

export function generateStaticParams() {
  return getAllPosts().map((p) => ({ slug: p.slug }))
}

// Only the posts above exist. Any other slug gets a 404 without rendering.
export const dynamicParams = false

export async function generateMetadata({ params }) {
  const { slug } = await params
  const post = getPost(slug)
  if (!post) return { title: 'Post not found' }
  return {
    title: post.title,
    description: post.excerpt,
    alternates: { canonical: `/blog/${slug}` },
    openGraph: { type: 'article', publishedTime: post.date },
  }
}

export default async function PostPage({ params }) {
  const { slug } = await params
  const post = getPost(slug)
  if (!post) notFound()

  return (
    <div className="container" style={{ maxWidth: 820 }}>
      <p className="breadcrumb"><Link href="/">Home</Link> › <Link href="/blog">Blog</Link></p>
      <article className="section">
        <h1 style={{ marginTop: 0 }}>{post.title}</h1>
        <p style={{ color: 'var(--muted)' }}>
          {new Date(post.date).toLocaleDateString('en-IN', { day: 'numeric', month: 'long', year: 'numeric' })}
        </p>
        {/* HTML comes from our own Markdown files, so it is safe to render. */}
        <div className="prose" dangerouslySetInnerHTML={{ __html: post.html }} />
      </article>
      <JsonLd
        data={{
          '@context': 'https://schema.org',
          '@type': 'BlogPosting',
          headline: post.title,
          description: post.excerpt,
          datePublished: post.date,
          author: { '@type': 'Organization', name: site.name },
          mainEntityOfPage: `${site.url}/blog/${slug}`,
        }}
      />
    </div>
  )
}
⚠️ गलत slugs पर असली 404

dynamicParams = false से generateStaticParams में न लौटे हर slug पर Next.js 404 देता है। ध्यान दें: हमने app/blog/ में loading.jsx नहीं रखा। [slug] के ऊपर loading file response को status 200 के साथ stream करना शुरू कर देती है, फिर बाद का notFound() status code नहीं बदल पाता — SEO के लिए बुरा।

.md file से web page तक

how-to-start-coding.mdgray-matterdata + contentmarked.parseHTML string[slug]/page.jsx.prose div/blog/how-to-start-codingstatic HTML
npm run build के दौरान एक बार चलता है

Result

EduSite blog list
/blog
Markdown से render हुआ EduSite blog post
/blog/how-to-start-coding

Checklist

  • ✅ Posts newest first sorted हैं।
  • ✅ /blog/does-not-exist और /blog/..%2F.. 404 लौटाते हैं।
  • ✅ View source में BlogPosting JSON-LD block और og:type = article दिखता है।
✅ Practice

Post page पर post के tags को badges के रूप में दिखाइए, और "Related posts" section जोड़िए जो दो दूसरे posts दिखाए।

अक्सर पूछे जाने वाले प्रश्न (FAQ)

क्या यहाँ dangerouslySetInnerHTML safe है?
हाँ, क्योंकि HTML आपकी खुद लिखी Markdown files से आता है। User input का HTML बिना sanitize किए इस तरह कभी render न करें।
नया post कैसे जोड़ें?
content/posts में front matter में title, date और excerpt के साथ नई .md file बनाइए, फिर rebuild कीजिए। वह list में आ जाएगा, उसका अपना page बनेगा और sitemap में जुड़ जाएगा।
← Next.js Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

अगला topicProject: Contact Form (Server Action) आपकी प्रगति: 34 / 37 (92%) · Lesson 35 / 37 आगे बढ़ें →

यह lesson कैसा लगा?

आपकी राय से हम content और बेहतर बनाते हैं।

अपना review और सुझाव लिखें (वैकल्पिक)

Review approve होने के बाद आपका नाम और शहर इसके साथ public दिख सकते हैं। Email या phone न लिखें।

▶ CodingEasily on YouTube

Video से सीखें – आसान Hindi में

चुनी हुई video: RDBMS भाग 3 – Class XI Commerce IT

▶ Channel subscribe करें