Advanced Features
JSON Export
Sticks the raw TranscriptData as JSON inside the HTML file so you can extract it later programmatically.
Option: includeJson: true
const html = generateTranscriptHtml(data, {
includeJson: true,
});
The JSON is embedded as a <script> tag with type application/json:
<script type="application/json" id="discord-transcript-json">
{"channel":{"id":"123","name":"general",...},"messages":[...]}
</script>
Extracting the data
In a browser:
const el = document.getElementById("discord-transcript-json");
const data = JSON.parse(el.textContent);
console.log(data.messages.length);
With Node.js (parsing the HTML file):
import { readFileSync } from "fs";
const html = readFileSync("transcript.html", "utf-8");
const match = html.match(
/<script type="application\/json" id="discord-transcript-json">([\s\S]*?)<\/script>/
);
if (match) {
const data = JSON.parse(match[1]);
console.log(`${data.messages.length} messages`);
}
Pagination
Breaks the transcript into pages with Previous/Next buttons at the bottom.
Option: messagesPerPage: <number>
const html = generateTranscriptHtml(data, {
messagesPerPage: 50,
});
- Splits messages into chunks of the given size
- Shows Previous / Page X of Y / Next at the bottom
- Only the current page is visible
- Works alongside search and other features
For very large transcripts (1000+ messages), pagination significantly improves browser performance since only one page of messages is rendered at a time.
Choosing a page size
| Messages | Recommended messagesPerPage |
|---|---|
| < 200 | No pagination needed |
| 200–500 | 100 |
| 500–2000 | 50 |
| 2000+ | 25–50 |
Custom Component Renderers
Let you swap out how specific component types get rendered. Handy if you want custom branding or need to handle a component differently.
Option: customRenderers: Record<number, (component: DiscordComponent) => string>
import { generateTranscriptHtml } from "@getoeteter/discord-transcripts";
import type { DiscordComponent } from "@getoeteter/discord-transcripts";
const html = generateTranscriptHtml(data, {
customRenderers: {
// Override button rendering (type 2)
2: (component: DiscordComponent) => {
const label = component.label || "Button";
return `<div class="my-custom-button">${label}</div>`;
},
// Override container rendering (type 17)
17: (component: DiscordComponent) => {
return `<div class="my-container">${component.content || ""}</div>`;
},
},
});
How it works
- Before rendering a component, we check if you've provided a custom renderer for that type
- If you have, we call it with the full
DiscordComponentobject - Your function needs to return an HTML string
- Child components are not automatically rendered - your custom renderer handles the full output
Component type reference
| Type | Name | Use case for override |
|---|---|---|
1 | ActionRow | Custom layout |
2 | Button | Custom styling or click handlers |
3 | StringSelect | Custom dropdown rendering |
9 | Section | Custom section layout |
10 | TextDisplay | Custom text rendering |
12 | MediaGallery | Custom gallery layout |
17 | Container | Custom container styling |
Custom renderers receive raw component data. You are responsible for sanitizing any user content in your output. Use HTML escaping for any text you render.