JSPM

better-discord-transcripts

2.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 2330
  • Score
    100M100P100Q10526F
  • License Apache-2.0

Discord HTML transcript generator with Components V2, threads, polls, voice playback, and search. Drop-in replacement for discord-html-transcripts.

Package Exports

  • better-discord-transcripts

Readme

better-discord-transcripts

npm version license node

A nicely formatted HTML transcript generator for discord.js v14/v15. Drop-in replacement for discord-html-transcripts, with Components V2, thread modals, voice playback, and a built-in search UI. Inspired by community forks such as discord.js-html-transcript.

Images and voice messages are inlined by default so transcripts stay readable offline. With sharp installed, images are compressed to WebP - much smaller HTML files.

Project status

In my opinion, the project is feature-complete for now. I will still update it regularly - bug fixes, discord.js changes, and small improvements as needed.

Requirements

  • Node.js 18+
  • discord.js ^14.26.4 or ^15 (peer dependency - install it yourself)
  • sharp (optional peer dependency - recommended for saveImages: true)

Sharp is not bundled. Install it yourself if you want WebP resize/compression (strongly recommended for ticket bots - without sharp, inlined transcripts can get very large).

Install

Base install (no image compression)

npm install better-discord-transcripts discord.js

package.json:

{
  "dependencies": {
    "better-discord-transcripts": "^2.0.0",
    "discord.js": "^14.26.4"
  }
}
npm install better-discord-transcripts discord.js sharp

package.json:

{
  "dependencies": {
    "better-discord-transcripts": "^2.0.0",
    "discord.js": "^14.26.4",
    "sharp": "^0.34.3"
  }
}

Migration from discord-html-transcripts

One line change. Same options, same return types:

- const { createTranscript } = require('discord-html-transcripts');
+ const { createTranscript } = require('better-discord-transcripts');

Your existing ticket code still works.

Quick start

Create a full channel transcript in 3 lines of code!:

const { createTranscript } = require('better-discord-transcripts');

const attachment = await createTranscript(channel, { limit: -1 });
await logChannel.send({ files: [attachment] });

If you omit limit (or set -1), the full channel history is fetched, including thread messages on posts with hasThread.

Image compression (sharp)

With default options, attachments and embed images are downloaded and embedded as WebP base64 instead of raw PNG/JPEG. That keeps transcript file size down while everything still opens from a single .html file with no external CDN links.

Setting Value
Library sharp (optional peer - npm install sharp)
Format WebP
Max dimension 720px (longest side)
Quality ~70%
Toggle saveImages: false skips download and inlining entirely
Sharp off useSharp: false keeps inlining but skips resize/WebP (raw base64)

Not recommended: turning sharp off (useSharp: false) while keeping saveImages: true. Images stay full resolution and original format (often PNG/JPEG), so a single ticket transcript can easily reach tens or hundreds of MB. Use it only as a fallback when sharp cannot load on your platform.

Sharp is probed when your bot loads the package (require('better-discord-transcripts')). If it is not installed or fails to load, you get a one-time warning at startup (with install instructions). During transcript generation the library falls back to the original image format and still produces HTML - but files may be much larger.

// Default - images compressed with sharp
await createTranscript(channel);

// Skip images - smallest output, but Discord CDN links may expire later
await createTranscript(channel, { saveImages: false });

// Fallback only - not recommended for production (transcripts can get very large)
await createTranscript(channel, { useSharp: false });

Options

Same options as discord-html-transcripts, plus saveVoiceMessages:

Option Type Default Description
limit number all messages Max messages to fetch. -1 or omit = everything
filter (m) => boolean none Filter messages before render
returnType 'attachment', 'buffer', 'string' 'attachment' Or use the ExportReturnType enum
filename string transcript-{channelName}.html Attachment filename
saveImages boolean true Download images and inline as WebP base64 via sharp (max 720px, ~70% quality)
useSharp boolean true Resize/compress inlined images with sharp; false = raw base64 (not recommended - huge HTML files)
saveVoiceMessages boolean true Download voice messages and inline as base64 so they stay playable after Discord URLs expire
footerText string Exported {number} message{s}. Footer line before the powered-by credit
poweredBy boolean true Show "Powered by better-discord-transcripts"
favicon 'guild' or string 'guild' Guild icon or a custom URL
hydrate boolean false Server-side hydration of web components (slower, works offline)
callbacks object auto resolveUser, resolveRole, resolveChannel, resolveImageSrc, resolveVoiceSrc

Disable image or voice inlining

// No images (voice messages still inlined by default)
await createTranscript(channel, { saveImages: false });

// Voice only (images stay as Discord CDN links, but links may break later - remember that!)
await createTranscript(channel, { saveImages: false, saveVoiceMessages: true });

// Neither - smallest file, but CDN links may break later - remember that!
await createTranscript(channel, { saveImages: false, saveVoiceMessages: false });

TypeScript

import { createTranscript, ExportReturnType } from 'better-discord-transcripts';

const transcript = await createTranscript(channel, {
  returnType: ExportReturnType.String,
});

generateFromMessages - when you already have messages

createTranscript fetches messages from Discord for you.

generateFromMessages skips that step. You pass a Message[] or Collection you already loaded (custom fetch, cached ticket log, filtered list, etc.) and get the same HTML output.

Use it when:

  • you fetched messages yourself (custom pagination, database, bot cache)
  • you want to render only a subset without calling the API again
  • you deleted the channel but still have message objects in memory
const { generateFromMessages } = require('better-discord-transcripts');

// messages = Message[] or Collection from channel.messages.fetch(), your DB, etc.
const attachment = await generateFromMessages(messages, channel, {
  poweredBy: false,
  filename: 'ticket-log.html',
});

Same options as createTranscript, except there is no limit or filter (you control the array yourself). saveImages, useSharp, and saveVoiceMessages default to true here as well.

Interactive HTML viewer

Built into every transcript:

  • Search - find messages by text or ID, keyboard navigation
  • Pinned messages - header panel with jump-to-message
  • Member list - participants and message counts
  • Thread modal - click a thread bar to read the full thread inline
  • Image lightbox - click images to zoom
  • Voice playback - play button and waveform (works offline when saveVoiceMessages is on)
  • Spoiler reveal - click to reveal spoilers

Content rendered

  • Plain text, markdown, mentions, emojis, timestamps
  • Embeds, attachments, stickers, reactions
  • Replies, forwards, slash command headers
  • Components V2 - Container, Section, TextDisplay, Media Gallery, Thumbnail, File, Separator, buttons
  • Polls with vote bars
  • Voice messages - waveform from attachment.waveform, duration, inline <audio>
  • System messages - pins, thread created, joins, and more
  • Server tags, role icons, verified bot badges
  • Cross-server reply indicators

Included by default

  • Full channel fetch when limit is omitted
  • Full thread fetch on messages with hasThread
  • Image inlining when saveImages: true (WebP via sharp when installed)
  • Images inlined as WebP base64 with sharp (720px max, ~70% quality)
  • Voice messages inlined as base64 (saveVoiceMessages: true) for offline playback

API

Export Description
createTranscript(channel, options?) Fetch messages from a channel and render HTML
generateFromMessages(messages, channel, options?) Render HTML from messages you already have
ExportReturnType Attachment, Buffer, or String
default { createTranscript, generateFromMessages }

Changelog

2.0.0 (latest)
  • Project marked as feature-complete; regular maintenance updates continue
  • Collapsible changelog section in README
  • Sharp probed at bot startup when the package is loaded
1.0.9
  • sharp moved to optional peer dependency (smaller install without image compression)
  • useSharp option to skip WebP resize/compression
  • isSharpAvailable() export and automatic fallback when sharp is missing
  • README install examples with and without sharp
1.0.8
  • Slightly darker embed and container backgrounds
  • Footer pill: Exported X messages | Powered by better-discord-transcripts
  • Unicode separators in UI changed to - where applicable
1.0.7
  • Lighter inline and block code background (#2b2d31) closer to Discord
1.0.6
  • Inline `code` styling like Discord (inherit font size, wrap-friendly background)
  • Fenced code blocks (```) match inline code style
1.0.5
  • Removed dead emoji registry code
  • CDN derock version fix (strip ^ from component version)
  • Select menu options render as <img> instead of raw URLs
1.0.4
  • hydrate: true by default for embed titles with custom emoji
  • Non-clickable emoji in messages, embeds, buttons, reactions, and polls
  • Embed title rendering fixes after hydration

License

Apache-2.0

Fork and successor of discord-html-transcripts by ItzDerock.