Mastering Content Planning with Notion for Web Projects

By · Updated

Turn Notion into a content production OS: plan topics, brief writers, track assets, and publish on time—without losing your mind (or your SEO).

Why Notion for Content Planning?

Notion shines when your team needs one source of truth for ideas, briefs, drafts, assets, and approvals. You get flexible databases, relations, and rollups—plus just enough automation to keep work moving. Pair it with a static site workflow and you’ve got a high-trust, low-bloat publishing machine.

  • Clarity: See every piece of content from idea → published.
  • Speed: Templates for briefs, outlines, and checklists.
  • Consistency: Required properties enforce your standards (SEO, accessibility, legal).
  • Scale: Relations/rollups stitch content, media, and people into one system.

Build Your Notion “Content OS” (3 Databases)

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.

Status values we use: Idea → Brief → Drafting → Editing → Design → Ready → Scheduled → Published → Update.

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.

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"))

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

Editorial Workflow (From Idea to Published)

  1. Idea: Add title, category, target keyword, intent, brief draft.
  2. Brief: Fill template; attach assets; assign writer + due date.
  3. Drafting: Writer completes outline → draft; checks internal links and SEO basics.
  4. Editing: Editor enforces tone, accuracy, accessibility, and legal disclaimers.
  5. Design: Create/attach images; confirm alt text + dimensions.
  6. Ready: Final pass; generate front-matter; mark issues resolved.
  7. Scheduled/Published: Map to PR/deploy; add canonical URL and publish date; set “Published.”
  8. Update: Auto-surface content to refresh after 180–365 days.

Cross-links: Topical Authority · Search Intent · Automation for Web Dev

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.

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?).

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.

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.

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.
Disclaimer: This article is provided for educational and informational purposes only and does not constitute legal, financial, or professional advice. All content is offered “as-is” without warranties of any kind. Readers are solely responsible for implementation and must ensure compliance with applicable laws and platform terms. Always apply the information only within authorized, ethical, and legal contexts.

Spot an error or a better angle? Tell me and I’ll update the piece. I’ll credit you by name—or keep it anonymous if you prefer. Accuracy > ego.

Portrait of Mason Goulding

Mason Goulding · Founder, Maelstrom Web Services

Builder of fast, hand-coded static sites with SEO baked in. Stack: Eleventy · Vanilla JS · Netlify · Figma

With 10 years of writing expertise and currently pursuing advanced studies in computer science and mathematics, Mason blends human behavior insights with technical execution. His Master’s research at CSU–Sacramento examined how COVID-19 shaped social interactions in academic spaces — see his thesis on Relational Interactions in Digital Spaces During the COVID-19 Pandemic . He applies his unique background and skills to create successful builds for California SMBs.

Every build follows Google’s E-E-A-T standards: scalable, accessible, and future-proof.