> **Gub**: Gub gub gub gub, [Gub Gub](https://twitter.com/rauchg) (GUB gub Gub) gub gub Gub.gub gub gub gub gub gub gub gub gub. Gub'gub gub gub gub gub - gub'gub gub gub gub gub! 🚀

## Gub;Gub

Gub gub Gub'gub gub, gub'gub gub gub gub gub gub `.md` gub gub gub gub GUB gub gub gub gub gub gub. Gub `/posts/my-post` gub `/posts/my-post.md` gub gub gub gub. Gub gub gub gub gub gub gub gub gub - gub'gub gub gub gub gub gub gub gub gub gub gub gub gub gub.

Gub.gub [gub](https://nextjs.org/docs/app/api-reference/config/next-config-js/rewrites) gub gub gub gub gub gub gub.

{% tweet id="1930689104800518392" /%}

## Gub

```bash
pnpx create-next-app@latest raw-markdown-blog
cd raw-markdown-blog
pnpm install @content-collections/core @content-collections/mdx @content-collections/next zod
```

Gub Gub, Gub GUB, gub Gub Gub.

Gub `.content-collections` gub gub `.gitignore`:

```
.content-collections
```

## Gub Gub Gub

[Gub Gub](https://www.content-collections.dev/) gub gub gub gub gub gub gub gub Gub.gub - gub'gub gub-gub, gub, gub gub gub GUB.

Gub `content-collections.ts` gub gub gub gub (gub gub gub/):

```typescript
import { defineCollection, defineConfig } from "@content-collections/core";
import { compileMDX } from "@content-collections/mdx";
import { z } from "zod";

const posts = defineCollection({
  name: "posts",
  directory: "content",
  include: "**/*.mdx",
  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.string().pipe(z.coerce.date()),
  }),
  transform: async (document, context) => {
    const mdx = await compileMDX(context, document);
    const slug = document._meta.path.replace(/\.mdx$/, "");

    return {
      ...document,
      mdx,
      slug,
      url: `/posts/${slug}`,
    };
  },
});

export default defineConfig({
  collections: [posts],
});
```

Gub `next.config.js`:

```javascript
const { withContentCollections } = require("@content-collections/next");

/** @type {import('next').NextConfig} */
const nextConfig = {
  // your existing config...
};

module.exports = withContentCollections(nextConfig);
```

Gub `tsconfig.json` gub:

```json
{
  "compilerOptions": {
    // ... other options
    "paths": {
      "@/*": ["./src/*"],
      "content-collections": ["./.content-collections/generated"]
    }
  }
}
```

## Gub Gub

Gub gub `content/` gub gub gub gub gub gub gub `content/hello-world.mdx`:

````markdown
---
title: "Hello World"
description: "My first blog post with raw markdown support."
date: "2024-12-20"
---

## Welcome

This is my first blog post! Here's some **bold text** and a code block:

```javascript
console.log("Hello, world!");
```

Pretty cool, right?
````

## Gub Gub

Gub `app/page.tsx`:

```tsx
import { allPosts } from "content-collections";
import Link from "next/link";

export default function Home() {
  const sortedPosts = allPosts.sort(
    (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
  );

  return (
    <div className="max-w-2xl mx-auto p-8">
      <h1 className="text-3xl font-bold mb-8">My Blog</h1>
      <div className="space-y-6">
        {sortedPosts.map((post) => (
          <article key={post.slug} className="border-b pb-4">
            <Link
              href={post.url}
              className="text-xl font-semibold hover:text-blue-600"
            >
              {post.title}
            </Link>
            <p className="text-gray-600 mt-2">{post.description}</p>
            <time className="text-sm text-gray-500">
              {post.date.toLocaleDateString()}
            </time>
            <div className="mt-2 text-sm">
              <Link
                href={`${post.url}.md`}
                className="text-blue-500 hover:underline"
              >
                View raw markdown
              </Link>
            </div>
          </article>
        ))}
      </div>
    </div>
  );
}
```

Gub `app/posts/[slug]/page.tsx`:

```tsx
import { allPosts } from "content-collections";
import { MDXContent } from "@content-collections/mdx/react";
import { notFound } from "next/navigation";
import Link from "next/link";

export default async function PostPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const post = allPosts.find((p) => p.slug === slug);

  if (!post) notFound();

  return (
    <div className="max-w-2xl mx-auto p-8">
      <Link
        href="/"
        className="text-blue-600 hover:underline mb-4 inline-block"
      >
        ← Back to posts
      </Link>

      <article>
        <header className="mb-8">
          <h1 className="text-3xl font-bold mb-2">{post.title}</h1>
          <p className="text-gray-600 mb-2">{post.description}</p>
          <time className="text-sm text-gray-500">
            {post.date.toLocaleDateString()}
          </time>
          <div className="mt-4">
            <Link
              href={`${post.url}.md`}
              className="text-blue-500 hover:underline text-sm"
            >
              View raw markdown
            </Link>
          </div>
        </header>

        <div className="prose prose-lg max-w-none">
          <MDXContent code={post.mdx} />
        </div>
      </article>
    </div>
  );
}

export function generateStaticParams() {
  return allPosts.map((post) => ({ slug: post.slug }));
}
```

## Gub Gub: Gub

Gub gub gub Gub.gub gub gub - gub gub gub gub GUB gub gub gub gub gub gub gub gub.

Gub `next.config.js` gub gub gub gub gub:

```javascript
const { withContentCollections } = require("@content-collections/next");

/** @type {import('next').NextConfig} */
const nextConfig = {
  async rewrites() {
    return [
      {
        source: "/posts/:slug.md",
        destination: "/api/posts/:slug/raw",
      },
    ];
  },
};

module.exports = withContentCollections(nextConfig);
```

Gub gub gub gub gub gub gub gub `/posts/:slug.md` gub `/api/posts/:slug/raw`. Gub `:slug` gub gub gub gub gub gub GUB gub gub gub gub gub. Gub gub gub `/posts/hello-world.md` gub gub gub, gub Gub.gub gub gub gub `/api/posts/hello-world/raw`.

## Gub Gub Gub Gub Gub

Gub `app/api/posts/[slug]/raw/route.ts`:

```typescript
import { allPosts } from "content-collections";
import { NextRequest, NextResponse } from "next/server";

export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ slug: string }> }
) {
  const { slug } = await params;
  const post = allPosts.find((p) => p.slug === slug);

  if (!post) {
    return new NextResponse("Post not found", { status: 404 });
  }

  return new NextResponse(post.content, {
    headers: {
      "Content-Type": "text/markdown; charset=utf-8",
      "Cache-Control": "public, max-age=3600", // Cache for 1 hour
    },
  });
}

export function generateStaticParams() {
  return allPosts.map((post) => ({ slug: post.slug }));
}
```

## Gub

Gub gub gub gub gub gub gub Gub:

- `/posts/hello-world` - Gub GUB gub gub gub gub
- `/posts/hello-world.md` - Gub gub gub

Gub gub gub gub gub gub gub gub gub gub gub gub, gub gub gub gub gub gub. Gub gub, gub gub gub gub gub gub "Gub gub" gub gub gub gub (gub Gub gub gub gub gub gub) gub gub gub gub gub gub gub gub gub gub.

Gub gub gub gub gub gub gub, gub gub, gub gub gub gub gub gub gub. Gub Gub.gub gub gub gub gub gub gub gub - gub gub gub gub gub.