Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions app/blog/[slug]/metadata.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { Metadata } from "next";
import { docs, meta } from "@/.source";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { siteConfig } from "@/lib/site";

const blogSource = loader({
baseUrl: "/blog",
source: createMDXSource(docs, meta),
});
import { Metadata } from "next";
import { siteConfig } from "@/lib/site";
import { blogSource } from "@/lib/blog-source";

export async function generateMetadata({
params,
Expand Down
15 changes: 4 additions & 11 deletions app/blog/[slug]/opengraph-image.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { ImageResponse } from "next/og";
import { docs, meta } from "@/.source";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { getAuthor, isValidAuthor, type AuthorKey } from "@/lib/authors";
import { ImageResponse } from "next/og";
import { getAuthor, isValidAuthor, type AuthorKey } from "@/lib/authors";
import { blogSource } from "@/lib/blog-source";

export const runtime = "nodejs";
export const alt = "Blog Post";
Expand All @@ -12,12 +10,7 @@ export const size = {
};
export const contentType = "image/png";

const blogSource = loader({
baseUrl: "/blog",
source: createMDXSource(docs, meta),
});

const getAssetData = async (authorAvatar?: string) => {
const getAssetData = async (authorAvatar?: string) => {
try {
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL;

Expand Down
9 changes: 1 addition & 8 deletions app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import { docs, meta } from "@/.source";
import { DocsBody } from "fumadocs-ui/page";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { notFound } from "next/navigation";
import { ArrowLeft } from "lucide-react";
import { Button } from "@/components/ui/button";
Expand All @@ -16,16 +13,12 @@ import { PromoContent } from "@/components/promo-content";
import { getAuthor, isValidAuthor } from "@/lib/authors";
import { FlickeringGrid } from "@/components/magicui/flickering-grid";
import { HashScrollHandler } from "@/components/hash-scroll-handler";
import { blogSource } from "@/lib/blog-source";

interface PageProps {
params: Promise<{ slug: string }>;
}

const blogSource = loader({
baseUrl: "/blog",
source: createMDXSource(docs, meta),
});

const formatDate = (date: Date): string => {
return date.toLocaleDateString("en-US", {
year: "numeric",
Expand Down
128 changes: 128 additions & 0 deletions app/page copy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { BlogCard } from "@/components/blog-card";
import { TagFilter } from "@/components/tag-filter";
import { FlickeringGrid } from "@/components/magicui/flickering-grid";
import { blogSource } from "@/lib/blog-source";

interface BlogData {
title: string;
description: string;
date: string;
tags?: string[];
featured?: boolean;
readTime?: string;
author?: string;
authorImage?: string;
thumbnail?: string;
}

interface BlogPage {
url: string;
data: BlogData;
}

const formatDate = (date: Date): string => {
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric",
});
};

export default async function HomePage({
searchParams,
}: {
searchParams: Promise<{ tag?: string }>;
}) {
const resolvedSearchParams = await searchParams;
const allPages = blogSource.getPages() as BlogPage[];
const sortedBlogs = allPages.sort((a, b) => {
const dateA = new Date(a.data.date).getTime();
const dateB = new Date(b.data.date).getTime();
return dateB - dateA;
});

const allTags = [
"All",
...Array.from(
new Set(sortedBlogs.flatMap((blog) => blog.data.tags || []))
).sort(),
];

const selectedTag = resolvedSearchParams.tag || "All";
const filteredBlogs =
selectedTag === "All"
? sortedBlogs
: sortedBlogs.filter((blog) => blog.data.tags?.includes(selectedTag));

const tagCounts = allTags.reduce((acc, tag) => {
if (tag === "All") {
acc[tag] = sortedBlogs.length;
} else {
acc[tag] = sortedBlogs.filter((blog) =>
blog.data.tags?.includes(tag)
).length;
}
return acc;
}, {} as Record<string, number>);

return (
<div className="min-h-screen bg-background relative">
<div className="absolute top-0 left-0 z-0 w-full h-[200px] [mask-image:linear-gradient(to_top,transparent_25%,black_95%)]">
<FlickeringGrid
className="absolute top-0 left-0 size-full"
squareSize={4}
gridGap={6}
color="#6B7280"
maxOpacity={0.2}
flickerChance={0.05}
/>
</div>
<div className="p-6 border-b border-border flex flex-col gap-6 min-h-[250px] justify-center relative z-10">
<div className="max-w-7xl mx-auto w-full">
<div className="flex flex-col gap-2">
<h1 className="font-medium text-4xl md:text-5xl tracking-tighter">
Magic UI Blog
</h1>
<p className="text-muted-foreground text-sm md:text-base lg:text-lg">
Latest news and updates from Magic UI.
</p>
</div>
</div>
{allTags.length > 0 && (
<div className="max-w-7xl mx-auto w-full">
<TagFilter
tags={allTags}
selectedTag={selectedTag}
tagCounts={tagCounts}
/>
</div>
)}
</div>

<div className="max-w-7xl mx-auto w-full px-6 lg:px-0">
<div
className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 relative overflow-hidden border-x border-border ${
filteredBlogs.length < 4 ? "border-b" : "border-b-0"
}`}
>
{filteredBlogs.map((blog) => {
const date = new Date(blog.data.date);
const formattedDate = formatDate(date);

return (
<BlogCard
key={blog.url}
url={blog.url}
title={blog.data.title}
description={blog.data.description}
date={formattedDate}
thumbnail={blog.data.thumbnail}
showRightBorder={filteredBlogs.length < 3}
/>
);
})}
</div>
</div>
</div>
);
}
54 changes: 22 additions & 32 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { docs, meta } from "@/.source";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import { Suspense } from "react";
import { BlogCard } from "@/components/blog-card";
import { TagFilter } from "@/components/tag-filter";
import { FlickeringGrid } from "@/components/magicui/flickering-grid";
import { blogSource } from "@/lib/blog-source";

interface BlogData {
title: string;
Expand All @@ -23,11 +20,6 @@ interface BlogPage {
data: BlogData;
}

const blogSource = loader({
baseUrl: "/blog",
source: createMDXSource(docs, meta),
});

const formatDate = (date: Date): string => {
return date.toLocaleDateString("en-US", {
year: "numeric",
Expand Down Expand Up @@ -108,30 +100,28 @@ export default async function HomePage({
</div>

<div className="max-w-7xl mx-auto w-full px-6 lg:px-0">
<Suspense fallback={<div>Loading articles...</div>}>
<div
className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 relative overflow-hidden border-x border-border ${
filteredBlogs.length < 4 ? "border-b" : "border-b-0"
}`}
>
{filteredBlogs.map((blog) => {
const date = new Date(blog.data.date);
const formattedDate = formatDate(date);
<div
className={`grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 relative overflow-hidden border-x border-border ${
filteredBlogs.length < 4 ? "border-b" : "border-b-0"
}`}
>
{filteredBlogs.map((blog) => {
const date = new Date(blog.data.date);
const formattedDate = formatDate(date);

return (
<BlogCard
key={blog.url}
url={blog.url}
title={blog.data.title}
description={blog.data.description}
date={formattedDate}
thumbnail={blog.data.thumbnail}
showRightBorder={filteredBlogs.length < 3}
/>
);
})}
</div>
</Suspense>
return (
<BlogCard
key={blog.url}
url={blog.url}
title={blog.data.title}
description={blog.data.description}
date={formattedDate}
thumbnail={blog.data.thumbnail}
showRightBorder={filteredBlogs.length < 3}
/>
);
})}
</div>
</div>
</div>
);
Expand Down
13 changes: 3 additions & 10 deletions components/read-more-section.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
/* eslint-disable @next/next/no-img-element */
import { docs, meta } from "@/.source";
import { loader } from "fumadocs-core/source";
import { createMDXSource } from "fumadocs-mdx";
import Link from "next/link";

const blogSource = loader({
baseUrl: "/blog",
source: createMDXSource(docs, meta),
});
/* eslint-disable @next/next/no-img-element */
import Link from "next/link";
import { blogSource } from "@/lib/blog-source";

const formatDate = (date: Date): string => {
return date.toLocaleDateString("en-US", {
Expand Down
8 changes: 8 additions & 0 deletions lib/blog-source.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { docs, meta } from "@/.source/server";
import { loader } from "fumadocs-core/source";
import { toFumadocsSource } from "fumadocs-mdx/runtime/server";

export const blogSource = loader({
baseUrl: "/blog",
source: toFumadocsSource(docs, meta),
});
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,28 @@
"@types/mdx": "^2.0.13",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"fumadocs-core": "^15.6.2",
"fumadocs-mdx": "^11.6.10",
"fumadocs-ui": "^15.6.2",
"fumadocs-core": "^16.6.7",
"fumadocs-mdx": "^14.2.8",
"fumadocs-ui": "^16.6.7",
"geist": "^1.4.2",
"lucide-react": "^0.525.0",
"lucide-react": "^0.575.0",
"motion": "^12.23.11",
"next": "15.3.5",
"next": "16.1.6",
"next-themes": "^0.4.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^3.3.1",
"vaul": "^1.1.2",
"zod": "^3.25.76"
"zod": "^4.3.6"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/node": "^25.3.3",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.5",
"eslint": "^10.0.2",
"eslint-config-next": "16.1.6",
"tailwindcss": "^4",
"tw-animate-css": "^1.3.5",
"typescript": "^5"
Expand Down
Loading