Files
Sveltey/docs/BLOG_SETUP.md
Luke Hagar b00c4a2da6 Overhaul
2025-05-27 17:09:58 -05:00

10 KiB

Markdown Blog Setup with MDSvex (Svelte 5 Compatible)

This project uses a static markdown-based blog system powered by MDSvex for SvelteKit. This implementation is optimized for Svelte 5 and uses component-based rendering instead of HTML content strings.

Features

  • Markdown Support: Write posts in markdown with full syntax support
  • Frontmatter Metadata: YAML frontmatter for post metadata
  • Featured Posts: Mark posts as featured for special display
  • Tags & Categories: Organize posts with tags
  • SEO Optimized: Automatic meta tags and Open Graph support
  • Responsive Design: Mobile-friendly blog layout
  • Syntax Highlighting: Code blocks with Shiki highlighting
  • Table of Contents: Automatic TOC generation
  • Svelte 5 Compatible: Uses component-based rendering

File Structure

src/
├── lib/
│   ├── posts/                           # Markdown blog posts
│   │   ├── getting-started.md
│   │   ├── advanced-features.md
│   │   └── building-integrations.md
│   ├── components/
│   │   └── MDXLayout.svelte            # Layout for mdx content
│   ├── blog.ts                         # Blog utilities and types
│   └── blog-utils.ts                   # Helper functions
├── routes/
│   └── blog/
│       ├── +page.svelte                # Blog index page
│       ├── +page.server.ts             # Load all posts
│       └── [slug]/
│           ├── +page.svelte            # Individual post page
│           └── +page.server.ts         # Load single post
└── docs/
    └── BLOG_SETUP.md                   # This documentation

Creating Blog Posts

1. Create a New Markdown File

Create a new .md file in src/lib/posts/:

touch src/lib/posts/my-new-post.md

2. Add Frontmatter

Start your post with YAML frontmatter:

---
title: "My Awesome Blog Post"
slug: "my-awesome-blog-post"
excerpt: "A compelling description of your blog post that will appear in listings and meta tags."
publishedAt: "2024-01-30"
author: "Your Name"
tags: ["tutorial", "sveltekit", "web-development"]
featured: true
---

# My Awesome Blog Post

Your content goes here...

3. Write Your Content

Use standard Markdown syntax for your blog post content:

## Section Heading

This is a paragraph with **bold** and *italic* text.

### Code Examples

```javascript
const example = () => {
  console.log('Hello from my blog!');
};

Lists

  • Item 1
  • Item 2
  • Item 3

Check out SvelteKit for more information.


## Frontmatter Reference

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `title` | string | ✅ | Post title displayed in listings and page title |
| `slug` | string | ✅ | URL-friendly identifier for the post |
| `excerpt` | string | ✅ | Short description for listings and meta tags |
| `publishedAt` | string | ✅ | Publication date in YYYY-MM-DD format |
| `author` | string | ✅ | Author name |
| `tags` | array | ✅ | Array of tags for categorization |
| `featured` | boolean | ❌ | Whether to feature this post (default: false) |

## Svelte 5 Integration

### Component-Based Rendering

This implementation uses Svelte 5's component system. Each markdown file is compiled to a Svelte component:

```typescript
export interface BlogPost {
  title: string;
  slug: string;
  excerpt: string;
  publishedAt: string;
  author: string;
  tags: string[];
  featured: boolean;
  component?: any; // The Svelte component for rendering
}

Rendering Posts

Posts are rendered using svelte:component:

<!-- In the blog post page -->
{#if post.component}
  <svelte:component this={post.component} />
{:else}
  <p>Content not available.</p>
{/if}

Blog Utilities

Available Functions

The blog system provides several utility functions in src/lib/blog.ts:

// Get all published posts
const posts = await getAllPosts();

// Get a specific post by slug
const post = await getPostBySlug('my-post-slug');

// Get featured posts only
const featured = await getFeaturedPosts();

// Get posts with a specific tag
const tagged = await getPostsByTag('tutorial');

// Get all unique tags
const tags = await getAllTags();

Helper Functions

Additional utilities in src/lib/blog-utils.ts:

// Generate URL-friendly slug from title
const slug = generateSlug('My Blog Post Title');

// Create a new post template
const template = createPostTemplate('New Post', 'Author Name');

// Validate post metadata
const errors = validatePostMetadata(postData);

// Search posts
const results = searchPosts(allPosts, 'keyword');

// Get related posts
const related = getRelatedPosts(currentPost, allPosts, 3);

Styling and Layout

Custom Prose Styles

The blog uses Tailwind's typography plugin with custom styling:

.prose {
  @apply text-surface-900-50-token max-w-none;
}

.prose h1 {
  @apply text-3xl font-bold mb-6;
}

.prose code {
  @apply bg-surface-200 dark:bg-surface-700 px-1 py-0.5 rounded;
}

MDX Layout Component

The MDXLayout.svelte component provides consistent styling for markdown content and can be customized for your design needs.

Configuration

MDSvex Configuration

The markdown processing is configured in svelte.config.js:

const mdsvexOptions = {
  extensions: ['.md'],
  layout: {
    _: './src/lib/components/MDXLayout.svelte'
  },
  remarkPlugins: [remarkUnwrapImages, remarkToc, remarkAbbr],
  rehypePlugins: [rehypeSlug],
  highlight: {
    highlighter: async (code, lang) => {
      const shiki = await import('shiki');
      const highlighter = await shiki.getHighlighter({
        themes: ['github-dark', 'github-light'],
        langs: ['javascript', 'typescript', 'html', 'css', 'svelte', 'bash', 'json', 'yaml', 'python', 'rust', 'go']
      });
      
      const html = highlighter.codeToHtml(code, {
        lang,
        themes: {
          light: 'github-light',
          dark: 'github-dark'
        }
      });
      
      return html;
    }
  }
};

Available Plugins

  • remark-unwrap-images: Removes paragraph wrappers around images
  • remark-toc: Generates table of contents from headings
  • remark-abbr: Processes abbreviation definitions
  • rehype-slug: Adds IDs to headings for anchor links
  • shiki: Syntax highlighting for code blocks

SEO Features

Automatic Meta Tags

Each blog post automatically generates:

  • Page title with post title
  • Meta description from excerpt
  • Open Graph tags for social sharing
  • Article metadata (published date, author, tags)

Example Generated Meta Tags

<title>My Blog Post | Blog</title>
<meta name="description" content="Post excerpt here" />
<meta property="og:title" content="My Blog Post" />
<meta property="og:description" content="Post excerpt here" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2024-01-30" />
<meta property="article:author" content="Author Name" />
<meta property="article:tag" content="tutorial" />

Development Workflow

1. Create New Post

# Create new post file
touch src/lib/posts/my-new-post.md

# Add frontmatter and content
# The post will automatically appear in the blog

2. Preview Posts

Posts are automatically available at:

  • Blog index: /blog
  • Individual post: /blog/your-post-slug

3. Manage Content

  • Posts are automatically sorted by publication date
  • Only posts with publishedAt dates in the past are shown
  • Featured posts appear in a special section

Advanced Features

Custom Components in Markdown

You can use Svelte components in your markdown:

<script>
  import MyComponent from '$lib/components/MyComponent.svelte';
</script>

# My Post

Here's a custom component:

<MyComponent prop="value" />

Dynamic Imports

Posts are loaded dynamically using Vite's import.meta.glob():

const allPostFiles = import.meta.glob('/src/lib/posts/*.md');

Build-Time Processing

All markdown processing happens at build time, ensuring:

  • Fast page loads
  • SEO-friendly static HTML
  • No runtime markdown parsing overhead

Svelte 5 Compatibility

Key Differences from Previous Versions

  1. Component-based rendering: Instead of HTML strings, posts are Svelte components
  2. No server-side rendering of content: Content is rendered client-side as components
  3. Simplified architecture: No need to handle HTML content strings

Migration from HTML-based Systems

If migrating from an HTML-content based blog:

  1. Update blog post interface to use component instead of content
  2. Replace {@html post.content} with <svelte:component this={post.component} />
  3. Remove any server-side HTML rendering logic

Deployment

The markdown blog works with any SvelteKit deployment target:

  • Static: Pre-rendered at build time
  • Server: Rendered on demand
  • Hybrid: Mix of static and server rendering

All posts are processed at build time, so they work perfectly with static deployment.

Best Practices

  1. Consistent Naming: Use kebab-case for slugs and filenames
  2. Descriptive Excerpts: Write compelling excerpts for better SEO
  3. Meaningful Tags: Use consistent, meaningful tags
  4. Proper Dates: Always use YYYY-MM-DD format for dates
  5. Image Optimization: Optimize images before including in posts
  6. Internal Linking: Use relative links for internal content

Troubleshooting

Common Issues

  1. Post not appearing: Check publishedAt date is not in the future
  2. Styling issues: Ensure prose classes are applied correctly
  3. Build errors: Validate frontmatter YAML syntax
  4. Component not rendering: Verify the markdown file has proper frontmatter

Debug Mode

In development, check the browser console for any post loading errors. The system will log issues with individual posts without breaking the entire blog.

Svelte 5 Specific Issues

  • Component rendering errors: Check that the markdown files are properly processed by mdsvex
  • Missing syntax highlighting: Ensure shiki is properly configured
  • Layout issues: Verify the MDXLayout component is correctly set in svelte.config.js

This Svelte 5 compatible markdown blog system provides a powerful, flexible, and maintainable solution for content management while leveraging the full power of Svelte's component system!