Creating the Obsidian Port to Blog

Welcome to my journey to figure out how to properly accomplish this. Weirdly enough it just dawned on me that programmer's tutorials, if theyre done as instructables or not just end up being ways for people to provide recipes, or help in navigating this space. So of course it would make sense to Feed Claude Your Notes. Anyways, besides that. let's fucking get into it.

Ideating or whatever the fuck

So initially i was like duh we gotta jekyll the fuck out of this. But I want to make a really fucking nice website, and for that then I obviously need to use Next.js or some form of js so that we can have cool components and use those fun little components that exist for all of us.

Now after looking online for different frameworks that are most likely to be able to generate the things I want to generate, I've come up with using Unified. Let's see how this shakes out. I realize that what I want to do is use Unified to then move on to whatever next.js Plus combo framework I want to.

Using Claude to Guide me through the creation of this system

I don't quite have that much experience with creating these kinds of pipelines, so naturally I look for one that will probably fit my use case the best, and then I run it. Now we are going to try to use Claude to figure out unified.js, which I really think is going to be good.

[!important] The following was generated by Claude with the following prompt using Sonnet 3.5

I NEED YOU to use your mcp server to scrape this: https://unifiedjs.com/learn/guide/introduction-to-unified/

then again mcp server to search for what you need to understand unified, then again scrape the urls you found for it.

I am going to want to use unified for this application.

then respond to the prompt as follows: I'm starting this new note. I want you to create a step by step directional artifact that I can use to learn and to understand how I need to figure out how to create this setup. I want it easily reproducible so I want it done minimally and simply because I know that I can customize it later.

I've also included some initial reference of other people doing it:

2025_02_21_Creating the Obsidian Port to Blog Status: #notes Tags: Web Development Programming Obsidian Zettelkasten Files

Creating the Obsidian Port to Blog

Welcome to my journey to figure out how to properly accomplish this. Weirdly enough it just dawned on me that programmer's tutorials, if theyre done as instructables or not just end up being ways for people to provide recipes, or help in navigating this space. So of course it would make sense to Feed Claude Your Notes. Anyways, besides that. let's fucking get into it.

Ideating or whatever the fuck

So initially i was like duh we gotta jekyll the fuck out of this. But I want to make a really fucking nice website, and for that then I obviously need to use Next.js or some form of js so that we can have cool components and use those fun little components that exist for all of us.

Now I realize that what I want to do is use Unified to then move on to whatever next.js Plus combo framework I want to.

Claude prompts and scripts

I did quite a bit of prompting and it worked pretty well, I think eventually I just used Trae, but this is how the blog worked or whatever. I ended up using unified js. I like open systems...

Anyways, here it was:

[!important] Claude Assisted Warning! Oftentimes it is important to note that I generated these with the help of Claude and my schema. These are often artifacts that are generated with the knowledge and a clear conversation about a certain event. These are typically artifacts that I've asked Claude to sum up the conversation and the new ideas presented. I've read through these thoroughly and very rarely if I do, but most likely would never publish a result of something I do not understand or do not agree with. If so there will be notes about it. I just don't want to go over and write what Claude can succinctly write about our ideation.

Building an Obsidian to Blog Pipeline with unified

Understanding the Pipeline

Our content will flow through this process:

  1. Obsidian Markdown → Parse (remark-parse)
  2. mdast → Transform (various plugins)
  3. mdast → hast (remark-rehype)
  4. hast → HTML (rehype-stringify)

Initial Setup

  1. Create project and install dependencies:
mkdir obsidian-blog
cd obsidian-blog
npm init -y

# Core dependencies
npm install unified remark-parse remark-rehype rehype-stringify

# Obsidian-specific plugins
npm install remark-wiki-link remark-frontmatter yaml
  1. Create the basic processor (src/processor.js):
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkWikiLink from 'remark-wiki-link'
import remarkFrontmatter from 'remark-frontmatter'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'

// Create base processor
export const processor = unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml'])
  .use(remarkWikiLink, {
    // Enable [[Internal-Links|Internal Links]]
    pageResolver: name => [name.replace(/ /g, '-').toLowerCase()],
    hrefTemplate: permalink => `/${permalink}`,
  })
  .use(remarkRehype)
  .use(rehypeStringify)

// Process a single file
export async function processContent(content) {
  const result = await processor.process(content)
  return String(result)
}
  1. Create a test script (src/test.js):
import { readFile } from 'fs/promises'
import { processContent } from './processor.js'

async function test() {
  const content = await readFile('test.md', 'utf8')
  const result = await processContent(content)
  console.log(result)
}

test().catch(console.error)
  1. Create a test file (test.md):
---
title: Test Post
date: 2025-02-21
tags: [test, blog]
---

# Test Post

This is a test with a [[Wiki-Link|Wiki Link]] and some *formatted* text.

Adding Obsidian-Specific Features

  1. Add support for Obsidian callouts (src/plugins/remarkCallouts.js):
import { visit } from 'unist-util-visit'

export function remarkCallouts() {
  return (tree) => {
    visit(tree, 'blockquote', (node) => {
      const firstChild = node.children[0]
      if (firstChild && firstChild.type === 'paragraph') {
        const text = firstChild.children[0].value
        if (text.startsWith('[!')) {
          const match = text.match(/\[!(.*?)\]/)
          if (match) {
            node.data = node.data || {}
            node.data.hProperties = {
              className: `callout callout-${match[1].toLowerCase()}`
            }
          }
        }
      }
    })
  }
}
  1. Add frontmatter extraction (src/plugins/remarkMetadata.js):
import { visit } from 'unist-util-visit'
import { load } from 'js-yaml'

export function remarkMetadata() {
  return (tree, file) => {
    visit(tree, 'yaml', (node) => {
      file.data.frontmatter = load(node.value)
    })
  }
}

Using with Next.js

  1. Create the content loader (src/lib/content.js):
import { readdir, readFile } from 'fs/promises'
import path from 'path'
import { processor } from '../processor'

const CONTENT_DIR = 'content'

export async function getAllPosts() {
  const files = await readdir(CONTENT_DIR)
  
  return Promise.all(
    files
      .filter(file => file.endsWith('.md'))
      .map(async (file) => {
        const content = await readFile(
          path.join(CONTENT_DIR, file),
          'utf8'
        )
        const result = await processor.process(content)
        
        return {
          slug: file.replace('.md', ''),
          content: String(result),
          frontmatter: result.data.frontmatter
        }
      })
  )
}

Next Steps

  1. Add support for more Obsidian features:

    • Graph visualization
    • Backlinks
    • Tag pages
    • Custom callouts
  2. Enhance the pipeline:

    • Add syntax highlighting (rehype-prism)
    • Add table of contents generation
    • Add image optimization
  3. Add development tools:

    • Watch mode for content changes
    • Development server
    • Build process

Usage Example

import { getAllPosts } from '../lib/content'

export default function Blog({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <article key={post.slug}>
          <h1>{post.frontmatter.title}</h1>
          <div dangerouslySetInnerHTML={{ __html: post.content }} />
        </article>
      ))}
    </div>
  )
}

export async function getStaticProps() {
  const posts = await getAllPosts()
  return { props: { posts } }
}

This setup gives you a basic pipeline that:

  • Handles Obsidian's wiki-links
  • Processes frontmatter
  • Supports basic Markdown features
  • Can be extended with more plugins

The unified ecosystem makes it easy to add more features incrementally as needed.

[!important] Another attempt below by Claude

Obsidian to Blog Pipeline Setup Guide

Quick Start

  1. Create a new Next.js project with the basic configuration:
# Create new Next.js project with TypeScript and other recommended settings
npx create-next-app@latest obsidian-blog --typescript --tailwind --eslint

# Navigate to project
cd obsidian-blog

# Install additional dependencies for Obsidian processing
npm install unified remark-parse remark-rehype rehype-stringify remark-wiki-link remark-frontmatter yaml unist-util-visit js-yaml
  1. Clone the starter template:
git clone https://github.com/[your-username]/obsidian-blog-starter.git
cd obsidian-blog-starter
  1. Set up your Obsidian vault:
  • Create a new folder called content in your project root
  • Create symbolic link from your Obsidian vault to the content folder
  • Add content to your .gitignore if you want to keep your vault private

Project Structure

obsidian-blog/
├── content/               # Your Obsidian vault (symlinked)
├── src/
│   ├── lib/
│   │   ├── processor.ts  # Unified processor setup
│   │   ├── content.ts    # Content loading utilities
│   │   └── types.ts      # TypeScript definitions
│   ├── plugins/
│   │   ├── remarkCallouts.ts
│   │   └── remarkMetadata.ts
│   ├── pages/
│   │   ├── index.tsx
│   │   ├── [slug].tsx    # Dynamic post pages
│   │   └── tags/[tag].tsx
│   └── components/
│       ├── MDContent.tsx # Markdown renderer
│       └── Layout.tsx
├── public/
├── package.json
└── next.config.js

Configuration Files

  1. next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  // Add any additional configuration here
}

module.exports = nextConfig
  1. src/lib/types.ts:
export interface Post {
  slug: string;
  content: string;
  frontmatter: {
    title: string;
    date: string;
    tags?: string[];
    [key: string]: any;
  };
}
  1. src/lib/processor.ts: [Previous processor code with TypeScript types]

Usage

  1. Start the development server:
npm run dev
  1. Create posts in your Obsidian vault using standard Markdown + Obsidian syntax:
---
title: My First Post
date: 2025-02-21
tags: [blog, tutorial]
---

# My First Post

This is a test with a [[Wiki-Link|Wiki Link]] and some *formatted* text.

> [!note] 
> This is an Obsidian callout

Known Limitations

  • Image processing requires additional setup
  • Graph visualization requires custom implementation
  • Some Obsidian plugins may not be supported

Next Steps

  1. Add image optimization:
npm install next-image-loader sharp
  1. Add syntax highlighting:
npm install rehype-prism-plus prismjs
  1. Add search functionality:
npm install flexsearch

Contributing

[Standard contributing guidelines]

License

MIT


For more detailed documentation and examples, visit the GitHub repository.