Why Use WordPress Table of Contents Without Plugin
Using a custom post type (CPT) instead of default posts gives you:
- Better content isolation
- Custom URL structure
(/case-study/,/insights/, etc.) - Dedicated schema control
- Performance benefits (no unnecessary filters or plugins)
Example CPT
register_post_type('case-study', [
'label' => 'Case Studies',
'public' => true,
'rewrite' => ['slug' => 'case-study'],
'supports' => ['title', 'editor', 'thumbnail'],
]);
Content Structure (Very Important for SEO)
Your blog must follow this hierarchy:
H1 β Blog Title (Only ONE) H2 β Main sections (used for TOC) H3 β Sub-sections Paragraphs Lists Images
Example
<h1>How We Increased Conversion Rate by 120%</h1>
<h2>Project Overview</h2>
<p>Brief description of the project scope.</p>
<h2>Challenges We Faced</h2>
<h3>Performance Issues</h3>
<h3>UX Problems</h3>
<h2>Our Solution</h2>
Rendered Output Example:
How We Increased Conversion Rate by 120%
Project Overview
We analyzed the existing funnel, user behavior, and technical bottlenecks affecting conversions.
Challenges We Faced
Performance Issues
Slow page load times and render-blocking assets were impacting user retention.
UX Problems
Poor CTA visibility and confusing navigation reduced engagement.
Our Solution
We optimized Core Web Vitals, restructured the layout, and improved CTA
placement.
Google uses H2s to understand page structure.
π‘ Your custom TOC reads H2 β perfect alignment.
Writing Content Using ACF (Best Practice)
Use ACF Flexible Content blocks like:
- Content Block
- Image + Content
- Quote Block
- Summary Block
- Author Block
Each block outputs real HTML headings, not shortcodes.
β
Clean
β
Crawlable
β
TOC-friendly
Pure Custom Table of Contents (No Plugin)
You already implemented this correctly π
What makes it SEO-safe:
- Reads real
<h2>headings - Generates real anchor links
- Uses
ItemListschema - No JavaScript dependency for content rendering
Why Google Likes This
- Anchors appear in SERP sitelinks
- Improves dwell time
- Improves page experience signals
TOC Schema (Rich Result Friendly)
Youβre using the correct schema:
{
"@type": "ItemList",
"name": "Table of Contents"
}
β
Supported
β
Safe
β No fake schema types
β No misuse of FAQ
This helps Google understand page navigation structure.
document.addEventListener('DOMContentLoaded', function () {
const contentWrap = document.querySelector(
'.blog-details-content > .col-right > .blog-details-content-part'
);
const toc = document.querySelector('#case-toc');
const tocList = toc?.querySelector('ul');
if (!contentWrap || !toc || !tocList) return;
const headings = contentWrap.querySelectorAll('h2');
if (!headings.length) {
toc.style.display = 'none';
return;
}
const HEADER_OFFSET = 100; // adjust for sticky header
const schemaItems = [];
/* -------------------------
* Build TOC + Schema
* ------------------------- */
headings.forEach((heading, index) => {
if (!heading.id) {
heading.id = `section-${index + 1}`;
}
/* ---------- TOC UI ---------- */
const li = document.createElement('li');
const a = document.createElement('a');
a.href = `#${heading.id}`;
a.textContent = heading.textContent;
a.addEventListener('click', function (e) {
e.preventDefault();
const targetPos =
heading.getBoundingClientRect().top +
window.pageYOffset -
HEADER_OFFSET;
window.scrollTo({
top: targetPos,
behavior: 'smooth'
});
});
li.appendChild(a);
tocList.appendChild(li);
/* ---------- Schema ---------- */
schemaItems.push({
"@type": "ListItem",
"position": index + 1,
"name": heading.textContent,
"url": window.location.href.split('#')[0] + '#' + heading.id
});
});
const tocLinks = tocList.querySelectorAll('a');
/* -------------------------
* Active link on scroll
* ------------------------- */
function setActiveHeading() {
let currentIndex = 0; // always keep one active
headings.forEach((heading, index) => {
const rect = heading.getBoundingClientRect();
if (rect.top <= HEADER_OFFSET + 10) { currentIndex = index; } }); tocLinks.forEach(link => link.classList.remove('active'));
if (tocLinks[currentIndex]) {
tocLinks[currentIndex].classList.add('active');
}
}
// Initial state
tocLinks[0]?.classList.add('active');
setActiveHeading();
window.addEventListener('scroll', setActiveHeading);
/* -------------------------
* Inject JSON-LD Schema
* ------------------------- */
const schema = {
"@context": "https://schema.org",
"@type": "ItemList",
"name": "Table of Contents",
"itemListElement": schemaItems
};
const script = document.createElement('script');
script.type = 'application/ld+json';
script.textContent = JSON.stringify(schema);
document.head.appendChild(script);
});
Build Faster, Rank Better β Without Plugins
Want a lightweight WordPress setup with custom CPTs, plugin-free Table of Contents, and clean schema that Google actually understands?
π Letβs build a high-performance WordPress site together
Get the free WordPress Security Checklist 2026
25-point checklist PDF β malware detection, hardening guide, login security. Used by 500+ WordPress site owners.
- β 25-point security checklist PDF
- β WordPress malware scan guide
- β Hardening checklist for any WordPress site
No spam. Unsubscribe any time.
You're in!
Check your inbox β the checklist PDF is on its way.
Need help with your WordPress site?
I'm a freelance WordPress developer who fixes exactly this kind of problem.
150+ projects. Clients in UK, US, UAE & Ireland. Fast turnaround.
Rajan Gupta
Freelance WordPress DeveloperRajan Gupta is a passionate web developer and digital creator who loves sharing insights on WordPress, modern web design, and performance optimization. When not coding, they enjoy exploring the latest tech trends and helping others build stunning, high-performing websites.