If you’re a developer looking to showcase your projects and share insights, having a portfolio combined with a blog is a powerful tool. In this guide, we’ll walk through building a Python-based static developer portfolio with a built-in Markdown blog, where both projects and posts are stored as files on disk and converted to HTML.
This approach gives you full control over your content, avoids complex CMS setups, and keeps your site lightweight and fast.
Technologies Used
- Python – for generating the site and rendering content.
- Markdown – for writing blog posts and project descriptions.
- HTML & CSS – for templates and styling.
- Python-Markdown (optional) – for converting Markdown files to HTML.
Step 1: Set Up Your Project
Create a project folder structure:
my_portfolio/
├─ posts/ # Markdown blog posts
├─ projects/ # Markdown project descriptions
├─ templates/ # HTML templates
├─ static/ # CSS and images
├─ generate.py # Python generator scriptInstall the Python-Markdown library:
pip install markdownStep 2: Write Markdown Content
Inside the posts folder, create files like first_post.md:
# My First Blog Post
This is an example of a Markdown post. It will be converted to HTML automatically.
Similarly, add project descriptions in projects/project1.md:
# My Cool Project
A short description of my project, its features, and technologies used.
Step 3: Create HTML Templates
Inside the templates folder, create base.html:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" href="../static/style.css">
</head>
<body>
<header>
<h1>{{ title }}</h1>
<nav>
<a href="index.html">Home</a> |
<a href="blog.html">Blog</a> |
<a href="projects.html">Projects</a>
</nav>
</header>
<main>
{{ content }}
</main>
</body>
</html>Step 4: Write the Python Generator
Create generate.py to convert Markdown to HTML:
import os
import markdown
TEMPLATES_DIR = 'templates'
POSTS_DIR = 'posts'
PROJECTS_DIR = 'projects'
OUTPUT_DIR = 'site'
os.makedirs(OUTPUT_DIR, exist_ok=True)
def render_template(template_path, context):
with open(template_path, 'r', encoding='utf-8') as f:
template = f.read()
for key, value in context.items():
template = template.replace(f'{{{{ {key} }}}}', value)
return template
def build_blog():
posts_html = ""
for filename in os.listdir(POSTS_DIR):
if filename.endswith('.md'):
filepath = os.path.join(POSTS_DIR, filename)
with open(filepath, 'r', encoding='utf-8') as f:
html_content = markdown.markdown(f.read())
posts_html += f"<article><h2>{filename.replace('.md','')}</h2>{html_content}</article><hr>"
html = render_template(os.path.join(TEMPLATES_DIR, 'base.html'), {'title': 'Blog', 'content': posts_html})
with open(os.path.join(OUTPUT_DIR, 'blog.html'), 'w', encoding='utf-8') as f:
f.write(html)
def build_projects():
projects_html = ""
for filename in os.listdir(PROJECTS_DIR):
if filename.endswith('.md'):
filepath = os.path.join(PROJECTS_DIR, filename)
with open(filepath, 'r', encoding='utf-8') as f:
html_content = markdown.markdown(f.read())
projects_html += f"<div class='project'><h2>{filename.replace('.md','')}</h2>{html_content}</div><hr>"
html = render_template(os.path.join(TEMPLATES_DIR, 'base.html'), {'title': 'Projects', 'content': projects_html})
with open(os.path.join(OUTPUT_DIR, 'projects.html'), 'w', encoding='utf-8') as f:
f.write(html)
def build_index():
content = "<p>Welcome to my developer portfolio!</p>"
html = render_template(os.path.join(TEMPLATES_DIR, 'base.html'), {'title': 'Home', 'content': content})
with open(os.path.join(OUTPUT_DIR, 'index.html'), 'w', encoding='utf-8') as f:
f.write(html)
if __name__ == "__main__":
build_index()
build_blog()
build_projects()
print("Site generated in ./site")
Step 5: Add Styling
Create static/style.css:
body {
font-family: Arial, sans-serif;
max-width: 900px;
margin: auto;
padding: 20px;
line-height: 1.6;
}
header {
text-align: center;
margin-bottom: 40px;
}
nav a {
margin: 0 10px;
text-decoration: none;
color: #333;
}
Step 6: Generate Your Site
Run the generator:
python generate.pyAll your pages will appear in the site folder as static HTML files, ready to be deployed.
Step 7: Next Steps
- Add metadata to Markdown files (author, date, tags).
- Generate individual pages per blog post.
- Add search or categories for better organization.
- Deploy the site on GitHub Pages, Netlify, or Vercel.
Conclusion
With just Python, Markdown, and a bit of HTML/CSS, you can create a full-featured static developer portfolio with a built-in blog. It’s easy to maintain, fast to load, and gives you total control over your content.