Post-rendering

Post-rendering (.post.ts files) lets you transform an HTML document after the build has already inlined every component, deduplicated styles, and compiled TypeScript. It runs as the last DOM step before the file is written to web/.

Use .post.ts when you need the final, fully-resolved DOM — for example to inject metadata derived from the rendered content, normalize attributes, or perform any cleanup that depends on components being expanded.

Quick Start

Create a .post.ts file with the same base name as your .html file:

// index.post.ts
const head = document.querySelector("head");
if (head) {
  const meta = document.createElement("meta");
  meta.setAttribute("name", "generator");
  meta.setAttribute("content", "tkeron");
  head.appendChild(meta);
}

The corresponding index.html is loaded after components have been resolved, and the result is written back to the same file.

How It Works

File Pairing

Post-rendering files are paired with HTML files by name:

Post-render FileProcesses
index.post.tsindex.html
about.post.tsabout.html
blog/post.post.tsblog/post.html

The document Variable

Every .post.ts file has access to a document variable — the fully-resolved DOM of the corresponding HTML:

document.querySelector("title");
document.getElementById("content");
document.querySelectorAll("article");
document.body;

All <my-component> tags have already been replaced, and duplicated component <style> blocks have already been deduplicated.

Build Process Order

1. Run .pre.ts          → Modify HTML before components run
2. Iterative components → .com.ts / .com.html / .com.md (up to 10 iterations)
3. Style deduplication  → One <style data-tk-com> per component
4. Run .post.ts         → Final DOM transformation
5. Compile TypeScript   → .ts → .js
6. Copy to output       → web/

.pre.ts vs .post.ts

.pre.ts.post.ts
RunsBefore componentsAfter components & style dedup
document containsRaw page + component tagsFully-inlined HTML
Can inject components?✅ Yes — they'll be processed next❌ No — would not be expanded
Typical useAPI fetching, dynamic markupFinal metadata, cleanup, analysis

Examples

Inject Final Build Metadata

// index.post.ts
const meta = document.createElement("meta");
meta.setAttribute("name", "build-time");
meta.setAttribute("content", new Date().toISOString());
document.querySelector("head")?.appendChild(meta);

Auto-link Headings

Because components are already expanded, you can safely scan the entire final document:

// docs.post.ts
document.querySelectorAll("h2, h3").forEach((heading) => {
  const id = heading.id;
  if (!id) return;
  const link = document.createElement("a");
  link.setAttribute("href", `#${id}`);
  link.textContent = "#";
  link.className = "heading-anchor";
  heading.appendChild(link);
});

Generate a Table of Contents

// docs.post.ts
const toc = document.getElementById("toc");
if (toc) {
  const items: string[] = [];
  document.querySelectorAll("h2[id]").forEach((h) => {
    items.push(`<li><a href="#${h.id}">${h.textContent}</a></li>`);
  });
  toc.innerHTML = `<ul>${items.join("")}</ul>`;
}

Limitations

For element-level dynamic generation use TypeScript Components. For build-time fetching before components run, use Pre-rendering.

Next Steps