SKILL.md
Converting Newsletter to Blog Post
A comprehensive guide for converting Substack (or Beehiiv) newsletter posts into properly formatted blog markdown files for the website.
Supported Platforms
- Substack (primary):
https://didierrlopes.substack.com/p/<slug>orhttps://substack.com/home/post/p-<id> - Beehiiv (legacy):
https://didierlopes.beehiiv.com/p/<slug>
Prerequisites
- Access to the newsletter URL
- Image extraction capabilities (use mcpfetchimageFetch tool)
- Understanding of the blog's markdown structure and front matter requirements
Step-by-Step Conversion Process
1. Extract Newsletter Content
Use the imageFetch tool to extract content and images:
mcp__fetch__imageFetch with url=<newsletter-url> and images={"output": "file", "layout": "individual", "maxCount": 10}
Platform-specific notes:
Substack:
- Content is usually well-extracted via imageFetch in markdown mode
- For posts accessed via
substack.com/home/post/p-<id>, the content may be embedded in JSON within the HTML. Useraw=trueand extract thebody_htmlfield from the embedded JSON - Clean Substack tracking params: remove
?utmsource=didierlopes.beehiiv.com&utmmedium=newsletter&utm_campaign=...from URLs - Substack image URLs follow the pattern:
https://substackcdn.com/image/fetch/.../https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F<uuid>_<dimensions>.<ext> - To download high-res images, construct URL:
https://substackcdn.com/image/fetch/w1200,climit,fpng,qauto:good/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F<uuid>_<dimensions>.<ext>
Beehiiv:
- Content is directly extractable via imageFetch
- Beehiiv CDN image URLs may expire, so download immediately
Key extraction requirements:
- Capture all text content including headings, paragraphs, and lists
- Extract all images in high quality (minimum 1000px width)
- Preserve link URLs and their context
- Note any special formatting (bold, italics, quotes)
- CRITICAL: The blog post content must be 1:1 with the original newsletter (excluding ads). Do not summarize, paraphrase, or restructure. Copy the exact content, structure, and formatting.
- IMPORTANT: Use WebFetch with a prompt to extract ALL important links:
- GitHub repository URLs (especially for open source project posts) - YouTube video URLs (these need to be embedded as iframes) - Links to other newsletter posts (convert to internal blog links if already converted) - Any other substantive links mentioned in the content
2. Get Publication Date
Format: YYYY-MM-DD
Substack:
- Fetch the archive page:
https://didierrlopes.substack.com/archive - Use raw mode and extract post titles alongside dates with pattern matching
- Dates appear as short format (e.g., "Feb 24", "Mar 6") next to post titles
- The slug may contain a date suffix (e.g.,
the-era-of-on-demand-software-26-01-31) but do NOT use it - it may not match the actual publication date - Always verify against the archive page
Beehiiv:
- Extract the publication date from https://didierlopes.beehiiv.com/ main page
- Find the newsletter post by title and get its publication date
Rules:
- Never use today's date - always use the actual publication date
- Create slug from title: lowercase, replace spaces with hyphens, remove special characters
- Example: "The trampoline job: Optimize your career for growth" →
2025-09-19-the-trampoline-job-optimize-your-career-for-growth.md
3. Create Front Matter
---
slug: <title-slug-without-date>
title: <Full Newsletter Title>
date: <YYYY-MM-DD>
image: /blog/YYYY-MM-DD-slug/hero.webp
tags:
- <relevant-tag-1>
- <relevant-tag-2>
- <relevant-tag-3>
description: <Newsletter subtitle or first paragraph summary (max 160 chars)>
hideSidebar: true
---
Front Matter Rules:
- slug: Use title in kebab-case without the date prefix
- title: Exact newsletter title, properly capitalized
- date: Newsletter publication date in YYYY-MM-DD format (from archive page, NOT today's date)
- image: Points to the hero image path inside the post asset folder (WITH
.webpextension). If the post has no cover image, omit this field - tags: Extract 3-6 relevant tags from content themes (lowercase)
- description: Use newsletter subtitle or create compelling summary
- hideSidebar: Set to
truefor blog posts
4. Process Images
IMPORTANT: All images must be in WebP format for optimal file size and fast page loads.
Image Handling Rules:
- Hero Image
- Substack: Fetch from the archive page (https://didierrlopes.substack.com/archive). Extract thumbnail image URLs which follow the pattern with public%2Fimages%2F<uuid>. Download at high resolution using: https://substackcdn.com/image/fetch/w1200,climit,fpng,qauto:good/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F<uuid><dimensions>.<ext> - Beehiiv: Fetch the hero image from https://didierlopes.beehiiv.com/ main page - Some posts may use YouTube thumbnails as cover images (URL pattern: https://substackcdn.com/image/youtube/w728,climit/<videoid>) - Download to /static/blog/YYYY-MM-DD-slug/hero.png (or .jpg) first, then convert to WebP - Note: The image must be saved in the post's static/blog/YYYY-MM-DD-slug/ directory, not just /blog/ - If a post has no cover image at all, omit the image: field from front matter
- Content Images
- Download to /static/blog/YYYY-MM-DD-slug/N.png (where N is sequential number) - Download from Substack/Beehiiv CDN URLs - Maintain aspect ratio - Note: Save in the post's static/blog/YYYY-MM-DD-slug/ directory
- Convert All Images to WebP
After downloading images, convert them to WebP format using: ```bash # For PNG/JPG images: cwebp -q 90 "image.png" -o "image.webp" && rm "image.png"
# For GIF images (animated): gif2webp -q 90 "image.gif" -o "image.webp" && rm "image.gif" ```
Final filenames should be: - Hero: /static/blog/YYYY-MM-DD-slug/hero.webp - Content: /static/blog/YYYY-MM-DD-slug/N.webp
- Image Markdown Format
```markdown 
<!-- For centered images with custom width --> <p align="center"> <img width="500" src="/blog/YYYY-MM-DD-slug/image-path.webp" alt="Description" /> </p> ```
5. Convert Content Structure
Content Conversion Rules:
- Opening Section
- Do NOT add hero image in the content (it's handled automatically via the front matter image field) - Include newsletter subtitle as opening paragraph - Add <!-- truncate --> after intro paragraph for blog preview
- Section Dividers
- Generally not needed between sections - Let content flow naturally without visual breaks - Only use if there's a major topic shift that requires clear separation
- Headings
- Newsletter H3 → Blog H2 (##) - Newsletter H4 → Blog H3 (###) - Maintain heading hierarchy
- Lists
- Preserve bullet points as markdown lists (-) - Maintain indentation for nested lists - Important: Add <br /> after each list block for proper spacing
Example: ```markdown - First item - Second item - Third item
<br />
Next paragraph starts here... ```
- Links
- Substack: Remove tracking parameters (?utmsource=...&utmmedium=...&utm_campaign=...) - Beehiiv: Convert Beehiiv tracking URLs to original URLs - Format: [link text](url) - For tweets/social embeds, reference as: This [post](url) - Convert links to other newsletter posts to internal blog links if already converted (e.g., https://didierlopes.com/blog/<slug>)
- Emphasis
- Bold text: text - Italic text: text
- Quotes and Citations
- Use blockquote syntax (>) for extended quotes - For multi-paragraph quotes, use > <br /> between paragraphs - Add <br /> after the quote block - IMPORTANT: Extract the actual hyperlink from the newsletter, not guess or create new ones - Include attribution with author name and link when available
Example: ```markdown > First paragraph of the quote goes here. > > <br /> > > Second paragraph of the quote continues here.
<br />
Author Name - "Article Title" ```
- Code/Technical Content
- Wrap technical terms in backticks: \term\ - Use code blocks for snippets
- YouTube Videos
- IMPORTANT: Extract the actual YouTube URL from the newsletter content - Convert YouTube links to embedded iframe format - Extract video ID from URL (e.g., https://www.youtube.com/watch?v=VIDEOID → VIDEOID) - Use only the video ID in the embed URL, no additional parameters - Use responsive embed with centered layout - IMPORTANT: Add <br /> after the video embed so following text doesn't appear glued to it
Example conversion: - Original: https://www.youtube.com/watch?v=Zyw-YA0k3xo - Embed format: ```html <div className="flex place-items-center justify-center items-center rounded-sm mx-auto"> <iframe src="https://www.youtube.com/embed/Zyw-YA0k3xo" width="800" height="400" /> </div>
<br /> ```
Note: Always verify the video link exists in the original content - don't assume or guess video IDs
- Image Captions
- If text immediately following an image is a caption/description of that image, style it differently - Use smaller font size and bring it closer to the image with negative margin
Example: ``html <p align="center"> <img width="800" src="/blog/YYYY-MM-DD-slug/image.webp" alt="Description" /> </p> <p align="center" style={{fontSize: '0.85em', marginTop: '-0.5em'}}>Caption text describing the image above.</p> ``
6. Content Cleanup
Remove from Newsletter:
- Footer/unsubscribe links
- Newsletter-specific CTAs (subscribe buttons, share widgets)
- Tracking parameters from URLs (
?utm_source=...) - Newsletter metadata (view in browser links)
- Substack "No posts" artifacts at the bottom
- "A quick note: I've moved this newsletter from Beehiiv to Substack" migration notices (unless contextually important)
Preserve:
- Author voice and tone
- All substantive content
- External references and citations
- Story flow and narrative structure
7. Quality Checks
Before finalizing:
- Front matter is complete and valid YAML
- Front matter
image:field includes.webpextension (or is omitted if no cover image) - All images are downloaded, converted to WebP, and properly referenced
- All image references in content use
.webpextension - Links are clean (no tracking parameters)
- Markdown syntax is valid
- Content flows naturally without newsletter artifacts
- File is saved in
/blog/directory with correct naming - Test render locally to ensure formatting
Example Conversions
Substack URL: https://didierrlopes.substack.com/p/the-context-wars-in-financial-services Converted to: /blog/2026-02-13-the-context-wars-in-financial-services.md
Beehiiv URL: https://didierlopes.beehiiv.com/p/the-trampoline-job-optimize-your-career-for-growth Converted to: /blog/2025-09-19-the-trampoline-job-optimize-your-career-for-growth.md
Key transformations:
- Extracted images and converted to WebP
- Converted newsletter sections to H2/H3 headings
- Cleaned URLs of tracking parameters
- Added proper front matter with relevant tags
- Preserved personal narrative and bullet points
Common Pitfalls to Avoid
- Don't include newsletter-specific language ("Click here to read more")
- Don't forget to download images (CDN links may expire)
- Don't use relative dates ("last week") - use specific dates
- Don't include email-specific formatting (table layouts for email clients)
- Don't forget the
<!-- truncate -->marker for blog preview - Don't trust date suffixes in Substack slugs - always verify against the archive page
- Don't use
substack.com/home/post/p-<id>URLs directly for content extraction - trydidierrlopes.substack.com/p/<slug>first as it renders better