Mastering Content Planning with Notion for Web Projects
This article is not sponsored...I am not that lucky!
Turn Notion into a content production processing center: plan topics, brief writers, track assets, and publish on time without losing your mind (well, mostly).
Why Notion for Content Planning?
Notion shines when your team needs one source of truth for ideas, briefs, drafts, assets, and approvals. As a programmer, I sincerely appreciate the concept of one file to reference to get the information I need. You get flexible databases, relations, and rollups — plus, just enough automation to keep work moving without feeling like you need to hire an administrative assistant. For me, I pair it with a static site workflow and I get a high-trust, low-bloat publishing machine — all without spending a dime.
- Clarity: easily see every piece of content throughout the entire publication process.
- Speed: adjust templates for briefs, outlines, and checklists.
- Consistency: the ability to set required properties enforce your standards (SEO, accessibility, legal).
- Scale: relations/rollups stitch content, media, and people into one system.
New to content systems? Start with Content Strategy for Service Sites and Building Out Pillar Pages, then wire those ideas into Notion.
Build Your Notion Supercenter
Database | Key Properties | Purpose |
---|---|---|
Content | Title (Title) · Status (Select) · Category (Select) · Tags (Multi-select) · Priority (Select) · Target Keyword (Text) · Search Intent (Select) · Word Count (Number) · Assignee (Relation: People) · Due Date (Date) · URL Slug (Formula) · Canonical URL (Formula) · Images (Relation: Assets) · Brief (Text) · CTA (Text) · Internal Links (Text) | Holds every article/page with SEO-critical metadata and workflow status. |
Assets | File (Files & media) · Alt Text (Text) · Usage (Relation: Content) · Dimensions (Text) · Compression (Select) · License (Select) · Source (URL) | Tracks images/diagrams: alt text, sizes, license, and where they’re used. |
People | Name (Title) · Email (Email) · Role (Select) · Rate (Number) · Assigned (Rollup from Content) · In-Progress (Formula) | Assigns writers, editors, SMEs; roll up workload for resourcing. |
Requests | Source (Select) · Requester (Text) · Topic (Title) · Use Case (Text) · Related Content (Relation) · Decision (Select) · Notes (Text) | Intake from sales/support so content maps to real questions and reduces churn. |
Status values we use: Idea → Brief → Drafting → Editing → Design → Ready → Scheduled → Published → Update
.
For site structure alignment, review Site Architecture for SEO Success and
Internal Linking Best Practices.
Essential Views (Board, Calendar, Editor)
- Pipeline (Board): Group by Status; show Assignee, Due Date, Priority.
- Calendar: By Due Date or Publish Date (Formula).
- Editor Queue (Table): Filter Editing/Design; show word count, assets attached, alt text complete.
- SEO Focus (Table): Filter where Target Keyword is not empty; show Search Intent, Internal Links, Canonical.
- Refresh (Saved Filter): Status = Update · Last Modified ≥ 180 days.
If you publish landing pages regularly, mirror these views against your conversion work: Writing Landing Pages that Convert.
Helpful Notion Formulas
// URL Slug (lowercase, hyphenated)
replaceAll(lower(prop("Title")), " ", "-")
// Canonical URL (prefix your domain)
"https://maelstromwebservices.com/blog/" + prop("Category") + "/" + prop("URL Slug") + "/"
// Publish Date (fallback to Due Date)
if(empty(prop("Published")), prop("Due Date"), prop("Published"))
// Needs Alt Text?
if(empty(prop("Images")), "No images", if(empty(rollup(prop("Images"), "Alt Text")), "Missing alt", "OK"))
// Title Guardrail (warn if too long for SERP)
if(length(prop("Title")) > 60, "Trim title", "OK")
// Refresh Trigger (180 days since last edit)
if(dateBetween(now(), prop("Last Edited Time"), "days") >= 180, "Needs refresh", "Fresh")
Deeper dive on formulas: Notion Formula Property.
Reusable Content Brief Template (inside each Content item)
Brief Outline
- Goal: (rank for X / convert Y)
- Primary Keyword: … | Intent: Informational / Commercial / Transactional / Local
- Secondary Terms: …
- Reader Persona: …
- Structure: H2/H3 map (TOC)
- Internal Links: pillars, clusters, locations
- External Sources: authoritative references (when helpful)
- Media: image list with alt text + dimensions
- CTA: primary / secondary
- Compliance: disclaimer needed? yes/no
For aligning keyword targets with intent, see Understanding Search Intent and Keyword Research for Service Businesses.
Editorial Workflow (From Idea to Published)
- Idea: Add title, category, target keyword, intent, brief draft.
- Brief: Fill template; attach assets; assign writer + due date.
- Drafting: Writer completes outline → draft; checks internal links and SEO basics.
- Editing: Editor enforces tone, accuracy, accessibility, and legal disclaimers.
- Design: Create/attach images; confirm alt text + dimensions.
- Ready: Final pass; generate front-matter; mark issues resolved.
- Scheduled/Published: Map to PR/deploy; add canonical URL and publish date; set “Published.”
- Update: Auto-surface content to refresh after 180–365 days.
Cross-links: Topical Authority · Search Intent · Automation for Web Dev · Optimizing Images for Performance · Best Tools for Static Site Builds
Automations That Save Hours
- Notion → GitHub PR: When Status = Ready, create a PR with markdown + YAML front-matter.
- Slack Notifier: On Status change to Editing/Design/Ready, ping the owner with due date.
- Image Pipeline: When Assets added, run sharp to produce 1200×630
.webp
with explicit width/height saved to front-matter. - SEO Gate: Block merge if alt text missing, meta description empty, or Lighthouse budgets fail.
API docs if you want to wire this from scratch: Notion API · GitHub Actions.
Example: Export Notion Page → Markdown (with Front-Matter)
Use the Notion API to pull a Content item and output a production-ready post for Eleventy/Astro/Hugo.
// scripts/notion-export.js (Node 20+)
import fs from "node:fs/promises";
import path from "node:path";
import { Client } from "@notionhq/client";
const notion = new Client({ auth: process.env.NOTION_TOKEN });
const DB_ID = process.env.NOTION_CONTENT_DB;
function slugify(s) { return s.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,""); }
function frontMatter(p) {
const title = p.properties.Title.title[0]?.plain_text ?? "Untitled";
const slug = p.properties["URL Slug"]?.formula?.string ?? slugify(title);
const desc = p.properties.Description?.rich_text?.[0]?.plain_text ?? "";
const tags = (p.properties.Tags?.multi_select ?? []).map(t => ` - ${t.name}`).join("\n");
const date = new Date().toISOString();
const image = "/assets/images/webp/blog/tools-workflow/tools-workflow_9.webp";
const canonical = `https://maelstromwebservices.com/blog/tools-workflow/${slug}/`;
return `---
title: "${title.replace(/"/g,'\\"')}"
description: "${desc.replace(/"/g,'\\"')}"
layout: layouts/default.njk
author: "Mason Goulding"
category: "tools-workflow"
tags:
${tags || " - content planning"}
date: ${date}
dateModified: ${date}
excerpt: >
${desc || "Post generated from Notion."}
image: "${image}"
imageWidth: 1200
imageHeight: 630
altText: "Notion content planning"
draft: false
canonicalUrl: "${canonical}"
slug: "${slug}"
readingTime: "6 min"
timeRequired: "PT6M"
wordCount: 1600
og_image: "${image}"
metaRobots: "index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"
schemaType: "BlogPosting"
inLanguage: "en-US"
---\n\n`;
}
const blocksToMd = (blocks) => blocks.map(b => {
const t = b.type;
if (t === "paragraph") return (b.paragraph.rich_text ?? []).map(rt => rt.plain_text).join("") + "\n\n";
if (t === "heading_2") return "## " + b.heading_2.rich_text.map(rt => rt.plain_text).join("") + "\n\n";
if (t === "heading_3") return "### " + b.heading_3.rich_text.map(rt => rt.plain_text).join("") + "\n\n";
return "";
}).join("");
async function run() {
const { results } = await notion.databases.query({ database_id: DB_ID, filter: { property: "Status", select: { equals: "Ready" } } });
for (const p of results) {
const pageId = p.id;
const title = p.properties.Title.title[0]?.plain_text ?? "untitled";
const slug = p.properties["URL Slug"]?.formula?.string ?? slugify(title);
const blocks = await notion.blocks.children.list({ block_id: pageId, page_size: 100 });
const md = blocksToMd(blocks.results);
const fm = frontMatter(p);
const out = path.join("src", "content", `${slug}.md`);
await fs.mkdir(path.dirname(out), { recursive: true });
await fs.writeFile(out, fm + md, "utf8");
console.log("Exported:", out);
}
}
run().catch(err => (console.error(err), process.exit(1)));
Wire this to GitHub Actions on a schedule or manual dispatch. Pair with your “ship” script to format, lint, build, and deploy.
Governance: Roles, Permissions, and Ownership
- Roles: Owner (you), Managing Editor, Writers, Designers, SMEs, Legal.
- Permissions: Editors can update metadata; writers draft-only; legal has comment-only access.
- Ownership: Each page has a single DRI; due dates are real; SLAs for review.
- Audits: Quarterly property audit (are required fields still right?).
If security or legal sign-off is part of your flow, keep this nearby: Subresource Integrity and All About Headers.
Metrics Dashboard (inside Notion)
- Production velocity: Published per week, average cycle time.
- Quality: % passing SEO + a11y checks; revisions per draft.
- Outcomes: Clicks/impressions (Search Console), conversions (analytics events).
- Refresh impact: Traffic delta 30/60 days post-update.
If you’re not measuring outcomes, you’re journaling. Connect your dashboard to Google Search Console and your analytics of choice.
Common Pitfalls (and Fixes)
- Property sprawl: Too many fields slow adoption. Start with 8–12 essentials; add later.
- No required metadata: Make Target Keyword, Intent, Meta Description mandatory at Ready status.
- Unlinked assets: Always relate images to the Content item; enforce alt text before “Ready.”
- Calendar drift: Use one canonical date (Publish) with formulas; sync to your deploys.
- Manual exports: Automate Notion → Markdown → PR to avoid copy/paste errors.
Want a lightweight stack for the publishing end? Start here: My Exact Web Design Stack.
Quick Setup Checklist
- Create Content, Assets, People databases with the properties above.
- Build views: Pipeline, Calendar, Editor Queue, SEO Focus, Refresh.
- Add the Brief Template as a Notion template (auto-insert on new Content).
- Wire automations: Status → PR, Slack pings, image pipeline, SEO gate.
- Publish dashboard: velocity, quality, outcomes, refresh impact.
Key Takeaways
- Notion becomes a Content OS when you model content, assets, and people with relations/rollups.
- Templates and status gates turn standards into muscle memory.
- Automate exports to markdown with front-matter to eliminate friction.
- Measure velocity and impact—refresh content on a schedule to compound gains.