This is a plugin for Remark to render Slack's mrkdwn format.
You can install this package using your favorite package manager!
# using pnpm
pnpm add remark-slack
# using bun
bun add remark-slack
ack
The main functionality of this package is as a Remark plugin. You can use it like so:
import { remark } from "remark";
import { remarkSlack } from "remark-slack";
const mrkdwn = `
*bold text*
_italic text_
_some *weird* combination ~of formatting~_
User ping: <@U1234567890>90>
:thumbsup:
:custom_emoji:
`;
const output = await remark().use(remarkSlack).process(mrkdwn);
console.log(String(output));
/*
Output:
**bold text**
*italic text*
*some **weird** combination*
User ping: <@U1234567890>90>
:thumbsup:
:custom_emoji:
*/Notice how the user ping and emojis don't get serialized - it is parsed as a custom node, which automatically de-serializes when turning it back into Markdown!
If you're using remark-rehype, you must use the rehypeSlack "plugin" to render to HTML. It outputs a configuration to rehype, which you can use as so:
import { remark } from "remark";
import remarkRehype from "remark-rehype";
import rehypeStringify from "rehype-stringify";
import { remarkSlack, rehypeSlack } from "remark-slack";
const mrkdwn = `
*bold text*
_italic text_
_some *weird* combination ~of formatting~_
User ping: <@U1234567890>90>
:thumbsup:
:custom_emoji:
`;
const processor = remark()
.use(remarkSlack)
.use(remarkRehype, rehypeSlack())
.use(rehypeStringify);
const html = await processor.process(mrkdwn);
console.log(String(html));
/*
Output:
bold textext</strong>
italic textt</em>
some weird combination ~of formatting~f formatting~</em>
User ping: @U1234567890t;/a></p>
👍t;👍
t;</p>
*/Optionally, you may pass a configuration object with the following keys (all optional):
-
component: boolean- see §Components
The following parameters are functions that, given some type of ID, should return a link. They only apply if component is set to false:
-
userLink: (userId: string) => stringing- this should usually be in the format ofhttps://[subdomain].slack.com/team/${userId}unless you want to link to some other page; by default, it points to a local fragment#U1234567890 -
channelLink: (channelId: string) => stringing- similar to the above, with links in the format ofhttps://[subdomain].slack.com/archives/${channelId}; by default, it points to a local fragment#C1234567890 -
emojiUrl: (code: string) => stringing- image to display for custom emojis. By default, it links to an instance of Cachet on the Hack Club workspace
By default, the rehype plugin outputs custom nodes as reasonable HTML elements. More specifically, user pings and channel links will become anchor tags.
However, if you would prefer to customize their behavior, you can set component: true to instead render them as custom HTML elements. This will process:
<@U1234567890>
<#C1234567890>
4567890>
Into:
slack-ping
class="slack-ping slack-ping-interactive"
data-user-id="U1234567890"
>gt;slack-ping>gt;
slack-channel
class="slack-channel slack-channel-interactive"
data-channel-id="C1234567890"
>gt;slack-channel>gt; To change the behavior, you can define a custom HTML element:
class SlackPing extends HTMLElement {
constructor() {
super();
}
async connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
const link = document.createElement("a");
const userId = this.getAttribute("data-user-id") ?? "";
link.innerText = "loading cool data";
link.href = `https://[subdomain].slack.com/team/${userId}`;
shadow.appendChild(link);
const style = document.createElement("style");
style.textContent = `[your cool styles here]`;
shadow.appendChild(style);
// ... cool data processing here ...
}
}
customElements.define("slack-ping", SlackPing);