Generate pixel-perfect, native-looking Discord HTML transcripts.
π Official Documentation β’
π₯οΈ Live Demo
Installation β’
Quick Start β’
Configuration β’
API Reference
This package is a completely modernized fork of discord-html-transcripts by ItzDerock, rebuilt from the ground up with a native Discord UI that is indistinguishable from the real thing.
| Feature | Description |
|---|---|
| π Forwarded Messages | Full support for Discord's forwarded message format with origin info |
| ποΈ Voice Messages | Waveform visualization with play button, duration, and speed controls |
| π Polls / Voting | Native poll rendering with answer options, vote counts, and progress bars |
| π·οΈ Server Tags | APP badges, role icons, and server tag badges on user profiles |
| π Buttons & Select Menus | Primary, secondary, success, danger, and link button styles + dropdown menus |
| π Invite Link Previews | Rich invite cards with server icon, member counts, and join button |
| πΌοΈ Image Preview Lightbox | Click any image to open a full-screen lightbox overlay |
| π Message Search | Built-in search bar to find messages within transcripts |
| π§΅ Thread Previews | Thread starter messages with preview boxes and participant info |
| π File Attachments | Native-styled file attachment cards with download buttons |
| π¨ ANSI Code Blocks | Full ANSI escape sequence rendering with colors and formatting |
| π± Fully Responsive | Mobile-first responsive design that works perfectly on all screen sizes |
| π Sticky Header | Channel header stays visible while scrolling, just like Discord |
| βοΈ Configurable Features | Toggle search, image preview, spoiler reveal, and more on/off |
| πΎ Granular Asset Saving | Fine-grained control over which remote assets (images, avatars, emojis, etc.) are preserved |
- Redesigned button and select menu components matching Discord's current design
- Modern dark theme background (
#070709) matching native Discord - Improved embed border styling with subtle transparency
- Enhanced markdown rendering (headings, lists, block quotes, code blocks)
- Responsive image galleries with multi-image grid layouts
- Native vote/poll UI with interactive styling
- Voice message capsule with amplitude-proportional waveform bars
- Custom file attachment cards with file type icons
# npm
npm install discord.js-html-transcript
# pnpm
pnpm add discord.js-html-transcript
# yarn
yarn add discord.js-html-transcriptRequirements: Node.js 16+ and discord.js v14 or v15
const discordTranscripts = require('discord.js-html-transcript');
const channel = message.channel;
const attachment = await discordTranscripts.createTranscript(channel);
channel.send({
files: [attachment],
});const discordTranscripts = require('discord.js-html-transcript');
const messages = await channel.messages.fetch({ limit: 100 });
const attachment = await discordTranscripts.generateFromMessages(messages, channel);
channel.send({
files: [attachment],
});import * as discordTranscripts from 'discord.js-html-transcript';
const attachment = await discordTranscripts.createTranscript(channel, {
returnType: discordTranscripts.ExportReturnType.Attachment,
filename: `transcript-${channel.id}.html`,
});Both createTranscript and generateFromMessages accept an options object.
All options are optional - sensible defaults are used when omitted.
const attachment = await discordTranscripts.createTranscript(channel, {
// ββ Output ββββββββββββββββββββββββββββββββββββββββββββββββ
returnType: 'attachment', // 'buffer' | 'string' | 'attachment'
filename: 'transcript.html', // output filename (when returnType is 'attachment')
// ββ Message Fetching (createTranscript only) ββββββββββββββ
limit: -1, // max messages to fetch (-1 = all)
filter: (message) => true, // filter which messages to include
// ββ Asset Preservation ββββββββββββββββββββββββββββββββββββ
saveImages: false, // legacy: save image attachments as data URIs
saveAssets: false, // save ALL remote assets as data URIs
assets: {
attachments: false, // images, videos, audio, and files
embeds: false, // embed media + author/footer icons
components: false, // media gallery items, thumbnails, component files
avatars: false, // user avatars
emojis: false, // custom emoji + twemoji assets
guildIcons: false, // header + favicon guild icon
inviteIcons: false, // invite preview server icons
roleIcons: false, // highest-role icon images
serverTagBadges: false, // server tag badge images
},
// ββ Feature Toggles βββββββββββββββββββββββββββββββββββββββ
features: {
search: true, // built-in message search UI
imagePreview: true, // click-to-open image lightbox
spoilerReveal: true, // click spoilers to reveal
messageLinks: true, // reply click-to-scroll behavior
profileBadges: true, // APP badges, server tags, role icons
embedTweaks: true, // client-side embed border/style fixes
},
// ββ Appearance ββββββββββββββββββββββββββββββββββββββββββββ
footerText: 'Exported {number} message{s}.',
poweredBy: true, // show "Powered by" footer credit
favicon: 'guild', // 'guild' or a custom URL string
hydrate: false, // server-side hydrate the HTML
// ββ Callbacks βββββββββββββββββββββββββββββββββββββββββββββ
callbacks: {
resolveChannel: (channelId) => channel.client.channels.fetch(channelId),
resolveUser: (userId) => channel.client.users.fetch(userId),
resolveRole: (roleId) => channel.guild?.roles.fetch(roleId),
resolveInvite: (code) => /* return { name, icon, online, members, url } */,
resolveImageSrc: (attachment, message) => /* return data URI or URL */,
resolveAssetSrc: (asset) => /* return data URI or URL */,
},
});Install sharp as a dev dependency to enable image compression:
npm install sharp --save-devconst { TranscriptImageDownloader } = require('discord.js-html-transcript');
const attachment = await discordTranscripts.createTranscript(channel, {
callbacks: {
resolveImageSrc: new TranscriptImageDownloader()
.withMaxSize(5120) // 5MB max per image (in KB)
.withCompression(40, true) // 40% quality, convert to webp
.build(),
},
});For transcripts that survive Discord CDN expiration:
const { TranscriptAssetDownloader } = require('discord.js-html-transcript');
const attachment = await discordTranscripts.createTranscript(channel, {
saveAssets: true,
// Or for fine-grained control:
assets: {
attachments: true,
embeds: true,
avatars: true,
emojis: true,
guildIcons: true,
},
callbacks: {
resolveAssetSrc: new TranscriptAssetDownloader()
.withMaxSize(10240) // 10MB per asset
.build(),
},
});| Normal Image | Multi Images | File Attachment |
|---|---|---|
![]() |
![]() |
![]() |
| Forwarded Image | Forwarded + Reactions |
|---|---|
![]() |
![]() |
| Poll | Thread | Mentions |
|---|---|---|
![]() |
![]() |
![]() |
| Buttons & Select Menu | Slash Command & Voice |
|---|---|
![]() |
![]() |
| Embed 1 | Embed 2 | Embed 3 |
|---|---|---|
![]() |
![]() |
![]() |
| Links | Invite Preview |
|---|---|
![]() |
![]() |
| Basic Formatting | Code Blocks | Headings |
|---|---|---|
![]() |
![]() |
![]() |
| Block Quotes | Lists | ANSI Styling |
|---|---|---|
![]() |
![]() |
![]() |
Fetches messages from a channel and generates an HTML transcript.
| Parameter | Type | Description |
|---|---|---|
channel |
TextBasedChannel |
The channel to export |
options |
CreateTranscriptOptions |
Configuration options (see above) |
Returns: Promise<AttachmentBuilder | Buffer | string>
const transcript = await discordTranscripts.createTranscript(channel, {
limit: 500,
filter: (msg) => !msg.author.bot,
returnType: 'attachment',
});Generates a transcript from a pre-fetched array of messages.
| Parameter | Type | Description |
|---|---|---|
messages |
Message[] | Collection<string, Message> |
Messages to include |
channel |
Channel |
Channel for header/guild info |
options |
GenerateFromMessagesOptions |
Configuration options (see above) |
Returns: Promise<AttachmentBuilder | Buffer | string>
const messages = await channel.messages.fetch({ limit: 100 });
const transcript = await discordTranscripts.generateFromMessages(messages, channel);Use the ExportReturnType enum for type-safe return values:
import { ExportReturnType } from 'discord.js-html-transcript';
// Returns a Buffer
const buffer = await discordTranscripts.createTranscript(channel, {
returnType: ExportReturnType.Buffer,
});
// Returns a string
const html = await discordTranscripts.createTranscript(channel, {
returnType: ExportReturnType.String,
});
// Returns an AttachmentBuilder (default)
const attachment = await discordTranscripts.createTranscript(channel, {
returnType: ExportReturnType.Attachment,
});const downloader = new TranscriptImageDownloader()
.withMaxSize(5120) // max file size in KB
.withCompression(40, true) // quality %, convert to webp
.build(); // returns a resolveImageSrc callbackconst downloader = new TranscriptAssetDownloader()
.withMaxSize(10240) // max file size in KB
.build(); // returns a resolveAssetSrc callbackconst discordTranscripts = require('discord.js-html-transcript');
const { TranscriptAssetDownloader } = require('discord.js-html-transcript');
client.on('messageCreate', async (message) => {
if (message.content === '!transcript') {
const transcript = await discordTranscripts.createTranscript(message.channel, {
returnType: 'attachment',
filename: `transcript-${message.channel.id}.html`,
limit: -1,
assets: {
attachments: true,
embeds: true,
avatars: true,
emojis: true,
guildIcons: true,
inviteIcons: true,
roleIcons: true,
serverTagBadges: true,
},
features: {
search: true,
imagePreview: true,
spoilerReveal: true,
messageLinks: true,
profileBadges: true,
embedTweaks: true,
},
callbacks: {
resolveAssetSrc: new TranscriptAssetDownloader().withMaxSize(10240).build(),
},
footerText: 'Exported {number} message{s}.',
poweredBy: true,
});
await message.reply({
content: 'π Here is your transcript!',
files: [transcript],
});
}
});| Feature | discord-html-transcripts |
discord.js-html-transcript |
|---|---|---|
| Basic Messages & Embeds | β | β |
| Forwarded Messages | β | β |
| Voice Messages | β | β |
| Polls / Voting | β | β |
| Server Tags & Role Icons | β | β |
| Buttons & Select Menus (Modern) | β | β |
| Discord Invite Previews | β | β |
| Image Preview Lightbox | β | β |
| Message Search | β | β |
| ANSI Code Block Styling | β | β |
| Thread Preview Cards | β | β |
| Fully Responsive Mobile UI | β | β |
| Sticky Channel Header | β | β |
| Feature Toggle System | β | β |
| Granular Asset Saving | β | β |
| Native File Attachment Cards | β | β |
| Image Compression (sharp) | β | β |
| Multi-Image Gallery Grid | β | β |
This package is built upon the excellent foundation of discord-html-transcripts by ItzDerock. Original styles powered by @derockdev/discord-components.
All new features, UI modernization, mobile responsiveness, and component redesigns by Wick Studio.
- π Discord Server: discord.gg/wicks
- π Bug Reports: GitHub Issues
- π¦ NPM: discord.js-html-transcript
This project is licensed under the Apache License 2.0.
Made with β€οΈ by Wick Studio





















