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

@sidvishnoi/imd

v0.0.2

Published

Tiny inline-markdown renderer for i18n strings, with pluggable backends.

Downloads

291

Readme

@sidvishnoi/imd

A tiny inline-markdown renderer for small strings, with support for React, Preact, DOM, and HTML strings.

Why

i18n strings often need a bit of inline formatting (a bold word, a link), but pulling in a full markdown parser for that is overkill, and hand-splitting strings around <a> tags in every component is repetitive and easy to get wrong across languages. imd gives translators a tiny, predictable syntax to write in, and gives you one md() call per rendering target that turns their string into real elements, with full control over how each piece (a link, a button, a custom tag) actually renders.

Usage

Install the package (npm i -S @sidvishnoi/imd), then import the backend for whatever you're rendering to. Each module exports the same shape: md(text, renderers?).

React

import { md } from '@sidvishnoi/imd/react';

const TEXT = 'Please review our [terms of service](/terms) before continuing.';

{md(TEXT, { link: (children, url) => (<a href={url} className="link">{children}</a>) })}
// Renders: Please review our <a href="/terms" class="link">terms of service</a> before continuing.

Preact

import { md } from '@sidvishnoi/imd/preact';

const TEXT = 'Your changes are *saved* automatically.';

{md(TEXT, { bold: (children) => <strong class="font-bold">{children}</strong> })}
// Renders: Your changes are <strong class="font-bold">saved</strong> automatically.

DOM

Returns Node[]; insert with container.replaceChildren(...).

import { md } from '@sidvishnoi/imd/dom';

const TEXT = "This can't be undone. <button>Delete account</button>";

el.replaceChildren(
  ...md(TEXT, {
    button: (children) => {
      const btn = document.createElement('button');
      btn.className = 'btn-danger';
      btn.append(...children);
      return btn;
    },
  }),
);
// el's markup is now:
// This can't be undone. <button class="btn-danger">Delete account</button>

HTML string

Returns a string; children arrive already joined, so you can interpolate them directly.

import { md } from '@sidvishnoi/imd/html';

const TEXT = 'Enter your <mark>API key</mark> to continue.';

const markup = md(TEXT, {
  html: ({ tag, children }) => `<${tag} class="highlight">${children}</${tag}>`,
});
// markup:
// 'Enter your <mark class="highlight">API key</mark> to continue.'

Every renderer above is optional. Anything you don't override falls back to a plain element (<a href="…">, <strong>, <em>) with no attributes added, so md(text) with no second argument always works. Renderers just let you attach classes, handlers, or a11y attributes at the call site.

Bold, italics and hyperlinks have a simple to use syntax:

*bold*       -> bold(children) => <strong>{children}</strong>
_italic_     -> italic(children) => <em>{children}</em>
[text](url)  -> link(children, url) => <a href="{url}">{children}</a>

For every raw-HTML string, use the html renderer to attach custom handlers:

<tag attr="…">…</tag>  -> html({ tag, children, attrs }) => <tag {...attrs}>{children}</tag>

For more examples (custom tags, button, the html catch-all, edge cases in parsing), see ./tests; each source module has a matching test file.

How it works

md() does two things under the hood.

First it parses: the string is scanned left to right for the four patterns above (link, bold, italic, custom tag). At each position the earliest match wins, and if two rules could match at the same spot, link beats bold beats italic beats a custom tag. This ordering is what gives the syntax its known quirks (see Caveats).

Then it renders: the resulting tree is walked node by node. For each element, it tries your renderer for that tag (link/bold/italic/button) first, then your generic html catch-all if you passed one, then falls back to a plain default element. The first one that doesn't return undefined wins.

All of this parsing and rendering logic lives once in a framework-agnostic core. react, preact, dom, and html are each just a few lines telling it how to construct an element, a text node, and how to combine a list of children for that target.

Caveats

  • There are no lists, headings, code blocks, or block-level anything, so this isn't CommonMark. It's just enough syntax for a translator to write click *here* or see [our terms](url) inside a single string.
  • Custom tags can't nest and can't self-close, and attribute values must be quoted; there's no escaping syntax either. This is a fixed grammar, not an extensible one.
  • Because matching is regex and position based rather than a real parser, delimiters are naive: 2 * 3 * reads as bold, and a_b_c reads as italic. That's fine for short, authored UI copy, but reword or restructure the source string if you hit it.
  • Treat these strings as authored content, not user input. This is meant for text your team or translators write, not text submitted by end users.