Moving from WordPress to 11ty
I have been using WordPress for a long time for my personal website. But these days, I love writing and taking notes inside VS Code so thought to move my personal blog from WordPress to the 11ty SSG.
The issue was, I had over 130 blog posts and manually converting them to Markdown and moving them to 11ty wasn't possible. But... I finally got it working, and you're reading this blog post on my new 11ty blog.
So... let me explain the entire process from the start. Here's a video that I recorded:
You can continue below, if you prefer to read instead.
1. Started with a 11ty starter
Initially, I thought of building the site from the scratch, but later I decided to use the official 11ty starter. I chose the starter theme and didn't build from scratch because it has a lot of featured already built-in, such as:
- Tags system
- XML sitemap
- RSS feed
- Image optimization
- Better navigation
- Fast loading, etc.
If I started from the scratch then it would have taken a lot more time than it did to customize the starter theme as per my requirements.
2. Customizing the starter theme
Once the theme was finalized, I started customizing and tweaking a few things in the theme. I didn't change a lot of things, though. In fact, let me explain what customizations I did.
Added my photo in the header
Adding the photo in the header was the most tricky part, because the pre-installed 11ty Image plugin was automatically optimizing the image and changing the image URL, and the image was broken in production. I spent a lot of time debugging the issue, and finally settled with adding eleventy:ignore
in the HTML, as shown below:
<a href="/" class="home-link">
<img eleventy:ignore src="/img/deepakness.jpg" alt="DeepakNess" />
DeepakNess
</a>
I could also have removed the image transform plugin, but I didn't want to as it's an amazing tool to optimize images.
Added social links and nav in the footer
Adding the footer navigation was easy, but I had to try multiple different icon community plugins. I finally settled with eleventy-plugin-phosphoricons as it had all social media and multiple different icons available. Below is how it looks in action:
Actually, I used Lucide icons as well, but didn't have the new icon for X (Twitter) so I had to look for an alternative, and Phosphor Icons was the best.
Other minor customizations
Apart from the above major ones, I did some other minor customizations, like:
- Removed the Feed link from the header and added the RSS icon in the footer
- Changed from showing 3 posts on the homepage to 5 posts
- Changed the default font (might change again, though)
- Added icons for publishing date and tags inside individual posts
- Edited the layout file to include open graph images
- Completely removed the dark mode from the
index.css
file, etc.
3. Moving blog posts from WordPress
I was all set to use the wordpress-export-to-markdown tool for converting all my posts to Markdown. But then I discovered Zach's new video where he demonstrates moving 100s of posts from WordPress to Markdown by using the command line tool @11ty/import
.
npx @11ty/import wordpress https://deepakness.com/ --output=content/blog --assetrefs=colocate
I just ran the above terminal command, and all my posts were copied as Markdowns into multiple sub-folders with associated images co-located in those folders. Amazing!
But there was an issue!
The frontmatters of the imported WordPress posts were messy. I didn't want authors
and metadata
and only wanted title
, date
, and tags
from it. I asked ChatGPT for a Python script that can clean the frontmatter of all .md
files across multiple sub-folders, and I got it working on the very first try. Below is the Python code, if you're interested:
import os
import yaml
import frontmatter
from pathlib import Path
from datetime import datetime
def clean_frontmatter(file_path):
print(f"\nReading file: {file_path}")
# Read the markdown file
try:
post = frontmatter.load(file_path)
print("Current frontmatter:", dict(post.metadata))
# Extract only needed frontmatter fields
cleaned_metadata = {
'title': post.get('title', ''),
'date': post.get('date', ''),
'tags': post.get('tags', [])
}
print("Cleaned frontmatter:", cleaned_metadata)
# Create new post with cleaned frontmatter
new_post = frontmatter.Post(
content=post.content,
**cleaned_metadata
)
# Write back to file
with open(file_path, 'w', encoding='utf-8') as f:
f.write(frontmatter.dumps(new_post))
print(f"✅ Successfully cleaned frontmatter")
except Exception as e:
print(f"❌ Error processing file: {str(e)}")
def process_markdown_files(directory):
# Convert to absolute path if it's relative
base_dir = Path(directory).resolve()
print(f"Looking for .md files in: {base_dir}")
# Check if directory exists
if not base_dir.exists():
print(f"❌ Directory not found: {base_dir}")
return
# Count files
md_files = list(base_dir.rglob('*.md'))
print(f"Found {len(md_files)} markdown files")
if len(md_files) == 0:
print("No markdown files found! Please check the directory path.")
return
# Process each file
for file_path in md_files:
clean_frontmatter(file_path)
if __name__ == "__main__":
# Use absolute path to content/blog/blog
current_dir = Path(__file__).parent
blog_directory = current_dir / "content" / "blog" / "blog"
print("Starting frontmatter cleanup...")
print(f"Script location: {current_dir}")
print(f"Target directory: {blog_directory}")
process_markdown_files(blog_directory)
Also, the import tool had created an additional
blog
folder inside thecontent/blog
folder. But that wasn't an issue, I manually copied all sub-folders to the correct folder.
I was surprised that I didn't get even a single error during the cleaning process. AI is getting a lot better at coding these days.
4. Making the website live
I pushed the code to a private GitHub repo and deployed the website on Netlify. The process was straightforward because I already know the process, but the first build time took around 4 minutes which was unusually high. Upon investigating, I found that the Eleventy image plugin transforms all images into multiple different formats during the build process and that takes a lot of time. The more number of images you have, it will take more time.
But the problem was, all images were getting transformed on every build which was unnecessary. I came across this repo while researching, but adding the suggested code in the netlify.toml
didn't help me.
Now, the build process is taking approx. 2:50 mins. but it should take even lesser time. When I remove the .avif
version and only keep the .webp
version, the build process only takes around 30 seconds. It could work, but it'd be better if AVIF version of images are also present.
So... I am still figuring out how to properly set up the cache plugin, and will update this section as I discover more.
I also had to add some 301 redirects as I deleted a few pages that I didn't want anymore. For this, I used the Netlify
_redirects
file approach.
5. Next steps
While I am pretty much satisfied with how the entire set up is, I still have a few things planned to do. Such as:
- Write a Python script that reads all the Markdown files, generates
description
for each, and then adds it in the frontmatter. I can use OpenAI'sgpt-4o-mini
model for this, and it shouldn't cost more than a dollar for generating descriptions for all posts I have.
That's it, for now!