npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@choice-ui/md-input

v0.0.4

Published

A markdown editor component with live preview, syntax highlighting, and markdown toolbar

Downloads

28

Readme

MD-Input Component

A production-ready markdown editor with GitHub Flavored Markdown (GFM) support, built with compound component pattern for maximum flexibility.

Features

  • ✅ Full GFM support (headings, lists, tables, code blocks, etc.)
  • ✅ Syntax highlighting with Shiki
  • ✅ Write/Preview tabs
  • ✅ Keyboard shortcuts (Cmd+B, Cmd+I, etc.)
  • ✅ Dark/Light theme support
  • ✅ Math equations (KaTeX)
  • ✅ XSS protection (harden-react-markdown)
  • ✅ Task lists with checkboxes
  • ✅ @Mention support with autocomplete
  • ✅ Optimized performance with caching
  • ✅ Compound component pattern for flexible composition

Component Architecture

The MdInput component uses a compound component pattern, allowing you to compose the editor layout as needed:

  • MdInput.Header - Header container for tabs and toolbar
  • MdInput.Tabs - Write/Preview tab switcher
  • MdInput.Toolbar - Formatting toolbar
  • MdInput.Container - Main content container (handles mentions popup)
  • MdInput.Editor - Textarea editor component
  • MdInput.Render - Markdown preview renderer
  • MdInput.Footer - Footer container

Basic Usage

Full Editor with Tabs and Toolbar

import { MdInput } from "@choice-ui/react"

function MyEditor() {
  const [value, setValue] = useState("")

  return (
    <MdInput
      value={value}
      onChange={setValue}
    >
      <MdInput.Header>
        <MdInput.Tabs />
        <MdInput.Toolbar />
      </MdInput.Header>
      <MdInput.Container>
        <MdInput.Editor placeholder="Enter markdown..." />
        <MdInput.Render />
      </MdInput.Container>
    </MdInput>
  )
}

Without Preview Tab

<MdInput
  value={value}
  onChange={setValue}
>
  <MdInput.Header>
    <MdInput.Toolbar />
  </MdInput.Header>
  <MdInput.Container>
    <MdInput.Editor />
  </MdInput.Container>
</MdInput>

Only Preview (Read-Only Display)

<MdInput value={markdownContent}>
  <MdInput.Render />
</MdInput>

Without Toolbar

<MdInput
  value={value}
  onChange={setValue}
>
  <MdInput.Header>
    <MdInput.Tabs />
  </MdInput.Header>
  <MdInput.Container>
    <MdInput.Editor />
    <MdInput.Render />
  </MdInput.Container>
</MdInput>

Mentions Feature

The component supports @mention functionality with autocomplete and filtering.

Basic Mentions

const mentionItems = [
  { id: "1", label: "John Doe" },
  { id: "2", label: "Jane Smith" },
  { id: "3", label: "Bob Johnson" },
]

<MdInput
  value={value}
  onChange={setValue}
  mentionItems={mentionItems}
>
  <MdInput.Header>
    <MdInput.Tabs />
    <MdInput.Toolbar />
  </MdInput.Header>
  <MdInput.Container>
    <MdInput.Editor placeholder="Type @ to mention someone..." />
    <MdInput.Render />
  </MdInput.Container>
</MdInput>

Custom Mention Format

const handleMentionSelect = (item: MentionItemProps, query: string) => {
  return `[@${item.label}](mailto:${item.email || ""}) `
}

;<MdInput
  value={value}
  onChange={setValue}
  mentionItems={mentionItems}
  mentionOnSelect={handleMentionSelect}
>
  {/* ... */}
</MdInput>

Custom Mention Rendering

const CustomMention = ({ mention }: MentionRenderProps) => {
  const user = mentionItems.find((item) => item.label === mention)
  return (
    <span className="bg-accent text-accent-foreground inline-flex items-center rounded px-1.5 py-0.5">
      @{mention}
    </span>
  )
}

;<MdInput
  value={value}
  onChange={setValue}
  mentionItems={mentionItems}
  mentionRenderComponent={CustomMention}
>
  {/* ... */}
</MdInput>

Keyboard Shortcuts

| Shortcut | Action | | -------------- | ------------------------------ | | Cmd+B | Bold | | Cmd+I | Italic | | Cmd+K | Insert link | | Cmd+\`` | Inline code | | Cmd+Shift+`| Code block | |Cmd+Shift+. | Blockquote | |Cmd+1-6 | Heading level | |Escape` | Close mention menu (when open) |

API

MdInput Props

interface MdInputProps extends Omit<HTMLAttributes<HTMLDivElement>, "onChange" | "children"> {
  value?: string
  onChange?: (value: string) => void
  disabled?: boolean
  readOnly?: boolean
  theme?: "light" | "dark"
  mentionItems?: MentionItemProps[]
  mentionOnSelect?: (item: MentionItemProps, query: string) => string
  mentionRenderComponent?: React.ComponentType<MentionRenderProps>
  allowedPrefixes?: string[]
  children: React.ReactNode // Required - must use compound components
}

MdInput.Header Props

interface MdInputHeaderProps {
  children?: React.ReactNode
  className?: string
}

MdInput.Tabs Props

interface MdInputTabsProps {
  className?: string
  i18n?: {
    preview: string
    write: string
  }
}

MdInput.Toolbar Props

interface ToolbarProps {
  groups?: ToolbarAction[][]
  visibleActions?: string[]
  visible?: boolean
  disabled?: boolean
  onAction?: (action: string) => void
  beforeElement?: React.ReactNode
  afterElement?: React.ReactNode
  children?: React.ReactNode
  className?: string
  i18n?: {
    heading: string
    bold: string
    italic: string
    quote: string
    code: string
    "code-block": string
    "unordered-list": string
    "ordered-list": string
    "task-list": string
  }
}

MdInput.Container Props

interface MdInputContainerProps {
  children?: React.ReactNode
  className?: string
}

MdInput.Editor Props

interface MdInputEditorProps extends Omit<TextareaProps, "value" | "onChange"> {
  className?: string
  minRows?: number
  maxRows?: number
  placeholder?: string
}

MdInput.Render Props

interface MdInputRenderProps {
  className?: string
  withScrollArea?: boolean
}

MdInput.Footer Props

interface MdInputFooterProps {
  children?: React.ReactNode
  className?: string
}

Types

interface MentionItemProps {
  id: string
  label: string
  [key: string]: unknown // Additional properties allowed
}

interface MentionRenderProps {
  mention: string
  mentionItems?: MentionItemProps[]
}

Advanced Usage

Custom Toolbar Actions

import type { ToolbarAction } from "@choice-ui/react"

const customGroups: ToolbarAction[][] = [
  [
    { id: "heading", label: "Heading", icon: <ParagraphHeading /> },
    { id: "bold", label: "Bold", icon: <FontBoldSmall /> },
  ],
]

<MdInput value={value} onChange={setValue}>
  <MdInput.Header>
    <MdInput.Tabs />
    <MdInput.Toolbar groups={customGroups} />
  </MdInput.Header>
  {/* ... */}
</MdInput>

Filtered Toolbar Actions

<MdInput.Toolbar visibleActions={["bold", "italic", "code"]} />

Custom Layout

<MdInput
  value={value}
  onChange={setValue}
>
  <MdInput.Header>
    <MdInput.Toolbar />
    <MdInput.Tabs />
  </MdInput.Header>
  <MdInput.Container>
    <MdInput.Editor />
    <MdInput.Render />
  </MdInput.Container>
  <MdInput.Footer>
    <div className="text-muted-foreground text-xs">Markdown supported</div>
  </MdInput.Footer>
</MdInput>

Toolbar in Footer

<MdInput
  value={value}
  onChange={setValue}
>
  <MdInput.Header>
    <MdInput.Tabs />
  </MdInput.Header>
  <MdInput.Container>
    <MdInput.Editor />
    <MdInput.Render />
  </MdInput.Container>
  <MdInput.Footer>
    <MdInput.Toolbar />
  </MdInput.Footer>
</MdInput>

Using Context API

import { useMdInputContext } from "@choice-ui/react"

function CustomComponent() {
  const { value, onChange, activeTab, setActiveTab, insertText, wrapText, insertListPrefix } =
    useMdInputContext()

  // Use context values...
}

Default Toolbar Actions

The default toolbar includes these action groups:

Group 1:

  • heading - Insert heading
  • bold - Bold text
  • italic - Italic text

Group 2:

  • quote - Blockquote
  • code - Inline code
  • code-block - Code block

Group 3:

  • unordered-list - Unordered list
  • ordered-list - Ordered list
  • task-list - Task list

Performance

  • Code highlighting is cached (up to 100 entries)
  • Toolbar icons are created once at module level
  • Preview components are memoized
  • Optimized re-renders with React.memo
  • Mention state updates are optimized to prevent unnecessary re-renders

Security

  • XSS protection via harden-react-markdown
  • Allowed link prefixes: https://, http://, #, mailto: (configurable via allowedPrefixes)
  • Allowed image prefixes: https://, http://, data:image/ (configurable via allowedPrefixes)

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

Dependencies

  • react-markdown - Markdown parsing and rendering
  • remark-gfm - GitHub Flavored Markdown
  • remark-math / rehype-katex - Math equations
  • shiki - Syntax highlighting
  • harden-react-markdown - Security hardening

License

MIT