Introduction
A component library, a design token system and an agent guide for building Discord-inspired interfaces in React, Vue and plain HTML.
newt/ui is a component library for building Discord-inspired interfaces — bots,
dashboards and docs that look like they belong next to the client. Every
component ships three times over: as plain HTML and CSS, as a React file, and as
a Vue single file component, all reading one set of --newt-* design tokens.
There is no package to install and no runtime to depend on. The CLI copies component source into your project, the same way shadcn/ui does, and stops there.
newt-message-groupimport { Avatar } from "@/registry/bases/newt/ui/avatar"
import {
MessageGroup,
MessageGroupAuthor,
MessageGroupAvatar,
MessageGroupBody,
MessageGroupBotTag,
MessageGroupContent,
MessageGroupEphemeralAction,
MessageGroupEphemeralNotice,
MessageGroupHeader,
MessageGroupReactions,
MessageGroupReply,
MessageGroupTime,
} from "@/registry/bases/newt/ui/message-group"
import { Reaction } from "@/registry/bases/newt/ui/reaction"
import {
ReplyPreview,
ReplyPreviewAuthor,
ReplyPreviewText,
} from "@/registry/bases/newt/ui/reply-preview"
import {
StatusDot,
StatusIndicator,
} from "@/registry/bases/newt/ui/status-indicator"
export default function MessageGroupDemo() {
return (
<div className="flex w-full max-w-md flex-col gap-2">
<MessageGroup withReply>
<MessageGroupReply>
<ReplyPreview>
<ReplyPreviewAuthor>@someone</ReplyPreviewAuthor>
<ReplyPreviewText>original message text</ReplyPreviewText>
</ReplyPreview>
</MessageGroupReply>
<MessageGroupAvatar>
<StatusIndicator>
<Avatar>U</Avatar>
<StatusDot status="online" />
</StatusIndicator>
</MessageGroupAvatar>
<MessageGroupBody>
<MessageGroupHeader>
<MessageGroupAuthor>username</MessageGroupAuthor>
<MessageGroupBotTag verified />
<MessageGroupTime dateTime="2026-09-04T14:23:00.000Z">
Today at 14:23
</MessageGroupTime>
</MessageGroupHeader>
<MessageGroupContent>Message content goes here.</MessageGroupContent>
<MessageGroupReactions>
<Reaction emoji="👍" count={1} />
</MessageGroupReactions>
</MessageGroupBody>
</MessageGroup>
<MessageGroup ephemeral>
<MessageGroupAvatar>
<Avatar>B</Avatar>
</MessageGroupAvatar>
<MessageGroupBody>
<MessageGroupHeader>
<MessageGroupAuthor>botname</MessageGroupAuthor>
<MessageGroupBotTag>APP</MessageGroupBotTag>
</MessageGroupHeader>
<MessageGroupContent>Command ran successfully.</MessageGroupContent>
<MessageGroupEphemeralNotice>
Only you can see this •
<MessageGroupEphemeralAction>
Dismiss message
</MessageGroupEphemeralAction>
</MessageGroupEphemeralNotice>
</MessageGroupBody>
</MessageGroup>
</div>
)
}<script setup lang="ts">
import { Avatar } from "~~/registry/bases/newt/ui/avatar"
import {
MessageGroup,
MessageGroupAuthor,
MessageGroupAvatar,
MessageGroupBody,
MessageGroupBotTag,
MessageGroupContent,
MessageGroupEphemeralAction,
MessageGroupEphemeralNotice,
MessageGroupHeader,
MessageGroupReactions,
MessageGroupReply,
MessageGroupTime,
} from "~~/registry/bases/newt/ui/message-group"
import { Reaction } from "~~/registry/bases/newt/ui/reaction"
import {
ReplyPreview,
ReplyPreviewAuthor,
ReplyPreviewText,
} from "~~/registry/bases/newt/ui/reply-preview"
import {
StatusDot,
StatusIndicator,
} from "~~/registry/bases/newt/ui/status-indicator"
</script>
<template>
<div class="flex w-full max-w-md flex-col gap-2">
<MessageGroup with-reply>
<MessageGroupReply>
<ReplyPreview>
<ReplyPreviewAuthor>@someone</ReplyPreviewAuthor>
<ReplyPreviewText>original message text</ReplyPreviewText>
</ReplyPreview>
</MessageGroupReply>
<MessageGroupAvatar>
<StatusIndicator>
<Avatar>U</Avatar>
<StatusDot status="online" />
</StatusIndicator>
</MessageGroupAvatar>
<MessageGroupBody>
<MessageGroupHeader>
<MessageGroupAuthor>username</MessageGroupAuthor>
<MessageGroupBotTag verified />
<MessageGroupTime datetime="2026-09-04T14:23:00.000Z">
Today at 14:23
</MessageGroupTime>
</MessageGroupHeader>
<MessageGroupContent>Message content goes here.</MessageGroupContent>
<MessageGroupReactions>
<Reaction emoji="👍" :count="1" />
</MessageGroupReactions>
</MessageGroupBody>
</MessageGroup>
<MessageGroup ephemeral>
<MessageGroupAvatar>
<Avatar>B</Avatar>
</MessageGroupAvatar>
<MessageGroupBody>
<MessageGroupHeader>
<MessageGroupAuthor>botname</MessageGroupAuthor>
<MessageGroupBotTag>APP</MessageGroupBotTag>
</MessageGroupHeader>
<MessageGroupContent>Command ran successfully.</MessageGroupContent>
<MessageGroupEphemeralNotice>
Only you can see this •
<MessageGroupEphemeralAction>
Dismiss message
</MessageGroupEphemeralAction>
</MessageGroupEphemeralNotice>
</MessageGroupBody>
</MessageGroup>
</div>
</template>What it is
Four decisions the whole registry is built on. They are why a component here is short enough to read before you paste it.
One token system, three targets
The tokens are declared once and every surface reads them: the plain HTML and CSS build, the React wrappers, and the Vue single file components. Change a token and all three move together.
You own the code
The CLI writes component source into your project and stops there. A component you need to bend is a file you already have, under Apache-2.0.
No motion by default
Hover, focus, online, do-not-disturb and loading are carried by colour, border and shape — the status dot states its mode by its outline, not by a pulse.
Accessible by construction
Body text meets WCAG AA contrast, every control is reachable by Tab with a visible focus ring, and presence is announced rather than left to colour alone.
Install
One CLI for both frameworks. It reads your project during init, then writes
component source into it.
pnpm dlx newtui@latest initnpx newtui@latest inityarn dlx newtui@latest initbunx newtui@latest initThen add what you need:
pnpm dlx newtui@latest add button embed member-listnpx newtui@latest add button embed member-listyarn dlx newtui@latest add button embed member-listbunx newtui@latest add button embed member-listWhere to go next
Start with the installation guide, which covers both CLIs and the Nuxt module; browse the components, each with a live example, the files it writes and the dependencies it needs; or read how to drive the registry from an agent in Using with AI.
newt/ui is an independent project. It is visually inspired by Discord’s client design but is not affiliated with, endorsed by, or sponsored by Discord Inc.