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
22 changes: 22 additions & 0 deletions apps/blog/public/img/logo-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/blog/public/img/logo-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/blog/public/img/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions apps/blog/source.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ import lastModified from 'fumadocs-mdx/plugins/last-modified';
import { z } from 'zod';
import convert from 'npm-to-yarn';

const blogPostTypes = [
'release',
'user-story',
'tutorial',
'community',
'postgres',
'changelog',
] as const;

export const blogPosts = defineCollections({
type: 'doc',
dir: 'content/blog',
Expand All @@ -21,6 +30,9 @@ export const blogPosts = defineCollections({
date: z.coerce.date(),
heroImagePath: z.string().optional(),
metaImagePath: z.string().optional(),
type: z.enum(blogPostTypes).optional(),
tags: z.array(z.string()).optional(),
featured: z.boolean().optional(),
}),
postprocess: {
includeProcessedMarkdown: true,
Expand Down
125 changes: 29 additions & 96 deletions apps/blog/src/app/(blog)/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { notFound } from 'next/navigation';
import Link from 'next/link';
import { InlineTOC } from 'fumadocs-ui/components/inline-toc';
import { getMDXComponents } from '@/mdx-components';
import { createRelativeLink } from 'fumadocs-ui/mdx';
import { BlogPostHeader } from '@/components/blog/blog-post-header';
import { BlogPostTOC } from '@/components/blog/blog-post-toc';
import { normalizePostMeta } from '@/lib/blog-data';
import { blog } from '@/lib/source';
import Image from 'next/image';
import { getMDXComponents } from '@/mdx-components';

export default async function Page(props: {
params: Promise<{ slug: string }>;
Expand All @@ -13,107 +13,40 @@ export default async function Page(props: {
const page = blog.getPage([params.slug]);

if (!page) notFound();

const post = normalizePostMeta(page);
const MDX = page.data.body;
const formatDate = (value: unknown) => {
const date =
value instanceof Date ? value : new Date((value as string) ?? '');
if (Number.isNaN(date.getTime())) return '';
return date.toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
});
};
const getHeroImageSrc = () => {
const data = page.data as any;
const rel =
(data.heroImagePath as string | undefined) ??
(data.metaImagePath as string | undefined);
if (rel) {
if (rel.startsWith('/')) return rel;
const base = page.url.startsWith('/') ? page.url : `/${page.url}`;
const baseClean = base.endsWith('/') ? base.slice(0, -1) : base;
const relClean = rel.replace(/^\.\//, '').replace(/^\/+/, '');
return `${baseClean}/${relClean}`;
}
const absolute =
(data.heroImageUrl as string | undefined) ??
(data.metaImageUrl as string | undefined);
return absolute ?? null;
};
const heroSrc = getHeroImageSrc();

return (
<>
{/* Hero image */}
{heroSrc ? (
<div className="w-full">
<div className="relative w-full aspect-video">
<Image
src={heroSrc}
alt={(page.data as any).heroImageAlt ?? page.data.title}
fill
priority
className="object-cover"
sizes="100vw"
<main className="mx-auto w-full max-w-[1040px]">
<div className="grid grid-cols-1 gap-8 lg:grid-cols-[minmax(0,1fr)_260px] lg:gap-10">
<article className="min-w-0">
<BlogPostHeader
author={post.author}
dateLabel={post.dateLabel}
description={post.description}
tags={post.tags}
title={post.title}
type={post.type}
/>
<BlogPostTOC className="mt-6 lg:hidden" items={(page.data.toc ?? []) as any[]} />
<div className="prose mt-6 max-w-none prose-headings:text-foreground prose-p:text-foreground/90 prose-li:text-foreground/90 prose-strong:text-foreground prose-a:text-primary prose-pre:rounded-xl">
<MDX
components={getMDXComponents({
a: createRelativeLink(blog, page),
})}
/>
</div>
</div>
) : null}

{/* Title + meta */}
<header className="w-full max-w-350 mx-auto px-4 md:px-8 py-10">
<Link href="/blog" className="text-fd-primary hover:underline text-sm">
← Back to Blog
</Link>
<h1 className="mt-3 mb-3 text-3xl md:text-4xl font-bold">
{page.data.title}
</h1>
{page.data.description ? (
<p className="text-fd-muted-foreground mb-3">{page.data.description}</p>
) : null}
<p className="text-sm text-fd-muted-foreground">
{page.data.authors?.length ? page.data.authors.join(', ') : null}
{page.data.date ? (
<>
{' • '}
<span>{formatDate(page.data.date)}</span>
</>
) : null}
</p>
</header>
</article>

{/* Body */}
<article className="w-full max-w-350 mx-auto flex flex-col px-4 md:px-8 pb-12">
<div className="prose min-w-0">
<InlineTOC items={page.data.toc} />
<MDX
components={getMDXComponents({
a: createRelativeLink(blog, page)
})}
<div className="relative hidden lg:block">
<BlogPostTOC
className="sticky top-[118px]"
items={(page.data.toc ?? []) as any[]}
/>
</div>
</article>

{/* Newsletter CTA */}
<div className="w-full max-w-350 mx-auto px-4 md:px-8 pb-16">
<div className="rounded-2xl border border-fd-primary/20 bg-fd-secondary p-6 md:p-8">
<h4 className="text-2xl font-semibold mb-2">
Don’t miss the next post!
</h4>
<p className="text-fd-muted mb-4">
Sign up for the Prisma Newsletter to stay up to date with the latest
releases and posts.
</p>
<Link
href="https://www.prisma.io/newsletter"
className="inline-flex items-center px-4 py-2 rounded-md bg-fd-primary text-white hover:opacity-90 transition"
>
Sign up
</Link>
</div>
</div>
</>
</main>
);
}

Expand Down
24 changes: 3 additions & 21 deletions apps/blog/src/app/(blog)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,5 @@
import { HomeLayout } from 'fumadocs-ui/layouts/home';
import { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
export function baseOptions(): BaseLayoutProps {
return {
nav: {
title: 'My App',
},
links: [
{
url: '/docs',
text: 'Docs',
},
{
url: '/blog',
text: 'Blog',
},
],
};
}
import { BlogShell } from '@/components/blog/blog-shell';

export default function Layout({ children, }: { children: React.ReactNode; }) {
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
export default function Layout({ children }: { children: React.ReactNode }) {
return <BlogShell>{children}</BlogShell>;
}
Loading
Loading