Personalize Your Notes Site Starter

· Updated

This starter is intentionally small, so most personalization happens in a handful of files. Work through this guide after cloning the repository to replace the example identity, add your projects and work experience, publish your own writing, and verify the production setup.

1. Install and run the project

The project requires Node.js 20.9 or newer and pnpm 11.

git clone https://github.com/qiyangdev/notes-site-starter.git
cd notes-site-starter
pnpm install
pnpm dev

Open http://localhost:3000 (opens in a new tab) and keep the development server running while you make changes.

2. Replace the public site URL first

Update baseUrl in app/sitemap.ts with the final production origin of your site:

export const baseUrl = 'https://YOUR_DOMAIN'

Use the origin only, without a trailing slash. This value is shared by page metadata, canonical URLs, article sharing images, JSON-LD, sitemap.xml, robots.txt, the RSS feed, and llms.txt. Setting it early prevents example URLs from leaking into search results, feeds, social previews, or machine-readable indexes.

If you do not have a custom domain yet, use the production URL assigned by your hosting provider and update it later.

3. Define your site identity

Edit app/layout.tsx and replace the starter metadata with your own:

  • Change the default title and title template.
  • Write a concise description of who you are and what you do.
  • Update the Open Graph title, description, and site name.
  • Change <html lang="en"> if your primary language is different.

For example:

export const metadata: Metadata = {
  metadataBase: new URL(baseUrl),
  title: {
    default: 'YOUR_NAME — Designer and Developer',
    template: '%s | YOUR_NAME',
  },
  description: 'YOUR_SHORT_BIO',
  openGraph: {
    title: 'YOUR_NAME',
    description: 'YOUR_SHORT_BIO',
    url: baseUrl,
    siteName: 'YOUR_NAME',
    locale: 'en_US',
    type: 'website',
  },
}

Keep the existing robots configuration unless you intentionally want to prevent indexing.

4. Rewrite the homepage, portfolio sections, and navigation

The homepage lives in app/page.tsx. Replace the heading and introduction with a short explanation of your work, then arrange the three provided sections in the order you want:

  • app/components/writing.tsx wraps the published article list.
  • app/components/projects.tsx renders project names, descriptions, years, and optional links.
  • app/components/work-experience.tsx renders companies, roles, and date ranges.

Projects and work experience share one data source in app/data/portfolio.ts. Replace the starter entries there instead of hard-coding content inside the components:

export const projects = [
  {
    name: 'My Project',
    description: 'A short explanation of what I built',
    year: '2026',
    href: 'https://example.com',
  },
]

export const workExperience = [
  {
    company: 'My Company',
    role: 'Design Engineer',
    period: '2024 — Present',
  },
]

Project links are optional. When href is present, the project name is clickable; when it is omitted, the component renders the name as plain text. Edit the corresponding component when you need different fields or presentation, and keep dates short enough to remain readable on narrow screens.

Content Collections indexes every .mdx file in app/blog/posts, and BlogPosts automatically supplies the Writing section with published entries, so no manual post index is required.

Navigation links are defined in app/components/nav.tsx. Add the routes that match your portfolio, remove links you do not need, and replace the starter deployment URL. A simple configuration might look like this:

const navItems = {
  '/': { name: 'home' },
  '/blog': { name: 'blog' },
  '/about': { name: 'about' },
}

Internal paths are rendered with Next.js navigation. Full URLs can also be used for external destinations.

Edit app/components/footer.tsx to replace:

  • The source repository URL
  • Any social or contact links
  • The copyright owner
  • Links that do not belong on your site

The page metadata and JSON-LD author are configured together in the articleAuthor object in app/blog/[slug]/page.tsx. Replace Qiyang and the GitHub profile URL with your own identity. If the publisher is an organization rather than a person, update the JSON-LD type accordingly.

The RSS channel title and description live in app/rss/route.ts. Update both so feed readers display your identity instead of the starter text.

Keep the MIT license and original copyright notice when redistributing the project. You may adapt the visible upstream link to your design, but preserve the attribution required by the repository license.

6. Publish your own posts

Create one .mdx file per article in app/blog/posts. The filename becomes the URL slug, so building-my-site.mdx is published at /blog/building-my-site.

Every post needs this frontmatter:

---
title: 'Building My Site'
publishedAt: '2026-08-09'
updatedAt: '2026-08-12'
summary: 'A short description used in metadata, feeds, and previews.'
draft: false
---

The optional updatedAt field records the date of a meaningful content update and must not be earlier than publishedAt. Omit it until the article changes; builds read this value but never rewrite the source file.

The draft field is optional and defaults to false. Set it to true to keep an unfinished post in the repository while excluding it from post lists, direct article routes, RSS, and the sitemap.

The collection schema and MDX plugins live in content-collections.ts. Update that file when you add frontmatter fields such as tags or categories; the development server and production build validate every article and generate the corresponding TypeScript types.

You can then write with Markdown or MDX. The renderer supports GitHub Flavored Markdown, syntax-highlighted code blocks with a copy button, tables, task lists, footnotes, and KaTeX math.

For standard Markdown images, place the files under public/images/blog and reference them from the site root. The shared MDX image component reads matching files at build time, so Next.js can infer their dimensions and optimize them without maintaining a manual image map:

![A descriptive alternative text](/images/blog/my-project.png)

The same automatic dimension lookup applies when you use the custom Image component with a root-relative public path, so dimensions are optional:

<Image
  alt="A descriptive alternative text"
  src="/images/my-project.png"
/>

For remote images, allow the host in next.config.ts and provide explicit width and height values.

To use a dedicated social image for a post, add an image field to its frontmatter:

image: '/images/my-project-social.png'

Remove app/blog/posts/gfm-rendering-test.mdx when you no longer need the rendering fixture. You can also remove this guide after publishing your own content.

7. Customize social previews

When a post does not define an image, the project generates a title-based Open Graph image through app/og/route.tsx. Adjust that route to match your visual identity:

  • Change the fallback title.
  • Add your name, logo, or role.
  • Update background, text colors, spacing, and typography.

Preview the result locally at /og?title=Preview%20Title before sharing a post.

8. Adjust typography and colors

Global typography is configured in app/layout.tsx with Geist and Geist Mono. Replace those next/font imports if you prefer another font, then update the corresponding variables in app/global.css.

The same stylesheet controls the page colors, prose elements, code highlighting, tables, blockquotes, and dark mode. Start with these areas:

  • ::selection for text selection colors
  • :root for syntax-highlighting tokens
  • @media (prefers-color-scheme: dark) for dark-mode overrides
  • .prose rules for article typography

Make small changes while viewing both light and dark mode, and check narrow screens whenever you change spacing or table styles.

9. Choose whether to keep analytics

Vercel Analytics and Speed Insights are mounted in app/layout.tsx. They require no extra configuration when the site is deployed to a supported Vercel project.

If you do not want them, remove the two imports and components from the layout, then uninstall their packages:

pnpm remove @vercel/analytics @vercel/speed-insights

You can deploy the site with another provider; the application itself does not depend on Vercel-specific hosting.

10. Update repository and machine-readable details

Before publishing your fork, update the project name in package.json and rewrite README.md with your own description, screenshots, repository URL, and deployment instructions. The README preview images live in .github/assets; keep their paths unchanged or update the matching <img> references. Keep the license file and required upstream copyright notice intact.

The root /llms.txt endpoint is generated by app/llms.txt/route.ts. Update its heading, summary, and page descriptions for your identity. Published writing is read from Content Collections, while Projects and Experience reuse app/data/portfolio.ts, so those lists stay synchronized with the homepage automatically. If you remove an entire homepage section, also remove its heading and mapping from the route rather than leaving an empty group.

Keep the root endpoint concise: it should point readers and tools to the canonical pages instead of duplicating complete article content.

Run a final search for starter-specific values:

rg -n "Notes Site Starter|Portfolio Starter Kit|My Portfolio|Qiyang|psk.qiyang.dev|qiyangdev" app README.md package.json

Review every match and decide whether it is site content to replace or attribution to preserve.

11. Verify before deployment

Run the same checks expected by the repository:

pnpm lint
pnpm typecheck
pnpm build

After the production build succeeds, inspect these routes:

  • / and /blog
  • At least one article page
  • /rss
  • /sitemap.xml
  • /robots.txt
  • /llms.txt
  • /og?title=Preview%20Title

Finally, deploy the site, view its page source or metadata with your browser tools, and confirm that every canonical URL and sharing image points to your production domain.

Once those checks pass, the starter is no longer a demo—it is your portfolio.