Skip to content

Commit

Permalink
Migrate to Next.js App Router (#52)
Browse files Browse the repository at this point in the history
* Create app directory and root layout

* Upgrade Next.js package

* Add nextjs-toploader so route progress works in app dir

* Port over icon/manifest metadata into layout

* Copy over body styles

* Add root providers

* Port over the main page layout and refactor pathname

* Move over analytics

* Migrate styles and use next optimized fonts

* Migrate the default seo metadata

* Add missing metadataBase option

* Convert index page

* Convert about page

* Move selectors into the page components

* Set meta title template

* Migrate blog index page

* Migrate blog slug page

* Migrate blog tags page

* Migrate project pages

* Remove _app/_document and all redundant packages/components

* Refactor sitemap generation

* Fix meta content

* Clean up

* Add back next-seo for JSON-LD support

* Update metadata
  • Loading branch information
melanieseltzer committed Aug 14, 2023
1 parent aefb816 commit 8b54033
Show file tree
Hide file tree
Showing 30 changed files with 658 additions and 662 deletions.
35 changes: 0 additions & 35 deletions next-sitemap.config.js

This file was deleted.

18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,9 @@
"format:check": "prettier --check .",
"format:fix": "prettier --write .",
"typecheck": "tsc --noEmit",
"generate-sitemap": "next-sitemap",
"generate-rss": "tsx ./scripts/generate-rss.ts",
"postinstall": "husky install && contentlayer build",
"postbuild": "yarn generate-sitemap && yarn generate-rss"
"postbuild": "yarn generate-rss"
},
"lint-staged": {
"*.+(js|ts|tsx)": [
Expand All @@ -28,21 +27,23 @@
"prettier --write"
]
},
"resolutions": {
"//eslint-plugin-react-hooks": "Fix: https://github.com/vercel/next.js/issues/52365",
"eslint-plugin-react-hooks": "4.6.0"
},
"dependencies": {
"@code-hike/mdx": "^0.8.1",
"@fontsource/inter": "^4.5.15",
"@headlessui/react": "^1.7.12",
"@vercel/analytics": "^0.1.11",
"clsx": "^1.2.1",
"contentlayer": "^0.3.0",
"feed": "^4.2.2",
"github-slugger": "^2.0.0",
"next": "^13.2.4",
"next": "^13.4.13",
"next-contentlayer": "^0.3.0",
"next-seo": "^5.15.0",
"next-sitemap": "^4.0.6",
"next-seo": "^6.1.0",
"next-themes": "^0.2.1",
"nprogress": "^0.2.0",
"nextjs-toploader": "^1.4.2",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-icons": "^4.7.1",
Expand All @@ -64,13 +65,12 @@
"@next/bundle-analyzer": "^13.2.1",
"@tailwindcss/typography": "^0.5.9",
"@types/node": "18.14.1",
"@types/nprogress": "^0.2.0",
"@types/react": "18.0.28",
"@types/react-dom": "18.0.11",
"autoprefixer": "^10.4.13",
"cross-env": "^7.0.3",
"eslint": "^8.34.0",
"eslint-config-next": "^13.2.1",
"eslint-config-next": "^13.4.13",
"husky": "^8.0.0",
"lint-staged": "^13.1.2",
"postcss": "^8.4.21",
Expand Down
45 changes: 25 additions & 20 deletions src/pages/about.tsx → src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import type { Metadata, ResolvingMetadata } from 'next';

import { Image } from '~/components/Image';
import { MDXComponent } from '~/components/MDXComponent';
import { PageIntro } from '~/components/PageIntro';
import { Prose } from '~/components/Prose';
import { SEO } from '~/components/seo';
import SocialLinks from '~/components/SocialLinks';
import { TechStack } from '~/components/TechStack';

import { getPageContent } from '~/content/page/client';
import type { Page } from '~/content/page/types';

import Avatar from '../../public/images/avatar.jpg';
import { siteMetadata } from '~/config/metadata';

import Avatar from '../../../public/images/avatar.jpg';

export async function generateMetadata(
// @ts-ignore throwaway
_,
parent: ResolvingMetadata
): Promise<Metadata> {
const parentOpenGraph = (await parent).openGraph || {};

return {
title: `About | ${siteMetadata.metaTitle}`,
description:
'Software Engineer and perpetual tinkerer specializing in front-end JavaScript development.',
openGraph: {
...parentOpenGraph,
title: 'About Me',
},
};
}

export default function AboutPage() {
const content = getPageContent('about');

export default function AboutPage({
content,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<>
<SEO
title="About Melanie Seltzer"
description="Software Engineer and perpetual tinkerer specializing in front-end JavaScript development."
/>

<PageIntro
heading="Hey there 👋 I'm Melanie"
subheading="Software Engineer, perpetual tinkerer, and relentlessly curious."
Expand Down Expand Up @@ -50,11 +63,3 @@ export default function AboutPage({
</>
);
}

export const getStaticProps: GetStaticProps<{ content: Page }> = () => {
const content = getPageContent('about');

return {
props: { content },
};
};
67 changes: 19 additions & 48 deletions src/pages/blog/[slug].tsx → src/app/blog/[slug]/BlogPostPage.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { ParsedUrlQuery } from 'querystring';
'use client';

import { GetStaticPaths, GetStaticProps, InferGetStaticPropsType } from 'next';
import { ArticleJsonLd } from 'next-seo';

import { MDXComponent } from '~/components/MDXComponent';
import { Prose } from '~/components/Prose';
import { BlogSEO } from '~/components/seo';
import { Spacer } from '~/components/Spacer';

import { getBlogPost, getBlogPosts } from '~/content/blog/client';
import { BlogPost } from '~/content/blog';
import { PublishedAndReadTime } from '~/content/blog/components/PublishedAndReadTime';
import { TagsList } from '~/content/blog/components/TagsList';
import type { BlogPost } from '~/content/blog/types';

import { siteMetadata } from '~/config/metadata';
import { authorMetadata, siteMetadata } from '~/config/metadata';
import { formatDate } from '~/utils/date';

export default function BlogPage({
post,
}: InferGetStaticPropsType<typeof getStaticProps>) {
interface Props {
post: BlogPost;
}

export function BlogPostPage({ post }: Props) {
const {
title,
summary,
Expand All @@ -31,15 +31,18 @@ export default function BlogPage({

return (
<>
<BlogSEO
<ArticleJsonLd
type="BlogPosting"
useAppDir
authorName={{
name: authorMetadata.name,
}}
url={`${siteMetadata.siteUrl}/blog/${slug}`}
title={title}
description={summary}
canonicalUrl={`${siteMetadata.siteUrl}/blog/${slug}`}
article={{
publishedTime: date,
modifiedTime: lastModified,
tags: tags.map(tag => tag.displayName),
}}
datePublished={date}
dateModified={lastModified}
images={[siteMetadata.images.socialBanner]}
/>

<Spacer size="8" />
Expand Down Expand Up @@ -83,35 +86,3 @@ export default function BlogPage({
</>
);
}

export const getStaticPaths: GetStaticPaths = () => {
const posts = getBlogPosts();

return {
paths: posts.map(({ slug }) => ({ params: { slug } })),
fallback: false,
};
};

interface Params extends ParsedUrlQuery {
slug: string;
}

type Props = {
post: BlogPost;
};

export const getStaticProps: GetStaticProps<Props, Params> = context => {
const slug = context.params!.slug;
const post = getBlogPost(slug);

if (!post) {
return {
notFound: true,
};
}

return {
props: { post },
};
};
64 changes: 64 additions & 0 deletions src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Metadata, ResolvingMetadata } from 'next';
import { notFound } from 'next/navigation';

import { getBlogPost, getBlogPosts } from '~/content/blog/client';

import { siteMetadata } from '~/config/metadata';

import { BlogPostPage } from './BlogPostPage';

interface Props {
params: { slug: string };
}

export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise<Metadata | undefined> {
const post = getBlogPost(params.slug);

if (!post) {
return;
}

const { title, summary, date, lastModified, tags, slug } = post;

const parentOpenGraph = (await parent).openGraph || {};

const url = `${siteMetadata.siteUrl}/blog/${slug}`;

return {
title,
description: summary,
alternates: {
canonical: url,
},
openGraph: {
...parentOpenGraph,
title,
description: summary,
type: 'article',
publishedTime: date,
modifiedTime: lastModified,
url,
tags: tags.map(tag => tag.displayName),
},
};
}

export const dynamicParams = false;

export function generateStaticParams() {
const posts = getBlogPosts();
return posts.map(({ slug }) => ({ slug }));
}

export default function Page({ params }: Props) {
const post = getBlogPost(params.slug);

if (!post) {
notFound();
}

return <BlogPostPage post={post} />;
}
53 changes: 26 additions & 27 deletions src/pages/blog/index.tsx → src/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
import { GetStaticProps, InferGetStaticPropsType } from 'next';
import type { Metadata, ResolvingMetadata } from 'next';

import { PageIntro } from '~/components/PageIntro';
import { Section } from '~/components/Section';
import { SEO } from '~/components/seo';
import { Spacer } from '~/components/Spacer';

import { getAllBlogTags, getLatestPosts } from '~/content/blog/client';
import { ExploreByTopic } from '~/content/blog/components/ExploreByTopic';
import { PostList } from '~/content/blog/components/PostList';
import type { BlogPostMetadata, Tag } from '~/content/blog/types';

export default function BlogIndexPage({
posts,
tags,
}: InferGetStaticPropsType<typeof getStaticProps>) {
import { siteMetadata } from '~/config/metadata';

export async function generateMetadata(
// @ts-ignore throwaway
_,
parent: ResolvingMetadata
): Promise<Metadata> {
const parentOpenGraph = (await parent).openGraph || {};
const metaDesc = 'Content focusing on React, JavaScript, Node.js, and more.';

return {
title: `Blog | ${siteMetadata.metaTitle}`,
description: metaDesc,
openGraph: {
...parentOpenGraph,
title: siteMetadata.siteName,
description: metaDesc,
},
};
}

export default function BlogIndexPage() {
const posts = getLatestPosts();
const tags = getAllBlogTags(posts);

return (
<>
<SEO
title="Blog"
description="Content focusing on React, JavaScript, Node.js, and more."
/>

<PageIntro
heading="Blog"
subheading="Thoughts, mental models, and notes on all things dev ✍️"
Expand All @@ -38,18 +52,3 @@ export default function BlogIndexPage({
</>
);
}

export const getStaticProps: GetStaticProps<{
posts: BlogPostMetadata[];
tags: Tag[];
}> = () => {
const posts = getLatestPosts();
const tags = getAllBlogTags(posts);

return {
props: {
posts,
tags,
},
};
};

1 comment on commit 8b54033

@vercel
Copy link

@vercel vercel bot commented on 8b54033 Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.