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

latex-cite-editor

v0.4.2

Published

A CodeMirror 6 based React editor with LaTeX-style \cite{} bibliography autocomplete backed by BibTeX (.bib) files

Readme

npm version npm downloads node types license

Live docs & examples →

Table of contents

Why

Writing a technical/academic article in Markdown usually means citations degrade to a manually numbered plain-text list, with no autocomplete and no link between an in-text reference and its entry. latex-cite-editor borrows the one LaTeX/BibTeX convention worth keeping — \cite{key} resolved against a .bib file — and wires it into a real editor: type \cite{ and get a live-filtered dropdown of your bibliography, sourced from whatever .bib text you feed it.

It does not compile real LaTeX or produce PDFs — it's a Markdown editing experience (built on CodeMirror 6) borrowing LaTeX authoring conventions that a physics/CS/math author already knows. Bring your own Markdown renderer (marked, remark, ...); this package only resolves \cite{...} into numbered, linked HTML markers and hands you back a ready-to-inject bibliography block.

Installation

npm install latex-cite-editor

Requires React 18+.

Quickstart

import { useMemo, useState } from 'react';
import { parseBibtex, resolveCitations } from 'latex-cite-editor';
import { CiteEditor } from 'latex-cite-editor/react';

function Editor() {
  const [content, setContent] = useState('');
  const [bibText, setBibText] = useState('');
  const bibEntries = useMemo(() => parseBibtex(bibText), [bibText]);

  return <CiteEditor value={content} onChange={setContent} bibEntries={bibEntries} />;
}

// When rendering the article:
const { text, bibliographyHtml } = resolveCitations(markdownSource, bibEntries);
const html = renderMarkdown(text) + bibliographyHtml; // plug into your own Markdown renderer

// Optionally format the bibliography list per a citation style (IEEE/MLA/APA/ABNT)
// instead of the default plain line, while keeping the [1][2][3] citation-order numbering:
const { bibliographyHtml: ieeeHtml } = resolveCitations(markdownSource, bibEntries, { style: 'ieee' });

See examples/basic-usage.tsx for a minimal editor + live preview, examples/article-editor-with-toolbar.tsx for the fuller pattern — a title/bibliography/content form with a formatting toolbar built entirely on CiteEditorHandle (no raw textarea/DOM access) — and examples/abnt-bibliography.tsx for rendering a whole .bib file as an ABNT-formatted reference list (see below).

ABNT bibliography formatting

Besides the \cite{}-driven numeric bibliography from resolveCitations, the package can format and sort an entire .bib file per ABNT NBR 6023 — the reference-list standard used across Brazilian academic work — independently of any \cite{} markers in your text:

import { parseBibtex, formatBibliographyAbnt } from 'latex-cite-editor';

const entries = parseBibtex(bibText);
const references = formatBibliographyAbnt(entries); // sorted alphabetically by author surname

function Bibliography() {
  return (
    <ol>
      {references.map((ref) => (
        <li key={ref.key} dangerouslySetInnerHTML={{ __html: ref.html }} />
      ))}
    </ol>
  );
}

Each reference comes out as SOBRENOME, Nome. **Título em negrito**. Local: Editora, Ano. (or the et al. form past three authors), with a layout matched to the entry's BibTeX type — book, article, incollection/inbook, inproceedings/conference, mastersthesis/phdthesis/ monografia/monography, and techreport each get their own clause order; anything else falls back to a generic title/place/year layout. Entries with a url/link/doi field (or a howpublished field, common on @misc) get a "Disponível em: ..." line, plus "Acesso em: ..." when an urlaccessdate field is present.

Since parseBibtex/formatBibliographyAbnt are plain functions with no React/CodeMirror dependency, this works the same in a React Server Component — see examples/abnt-bibliography.tsx, which resolves the bibliography at render time on the server, no 'use client' needed.

IEEE bibliography formatting

import { parseBibtex, formatBibliographyIeee } from 'latex-cite-editor';

const entries = parseBibtex(bibText);
const references = formatBibliographyIeee(entries); // preserves input order — see note below

function Bibliography() {
  return (
    <ol>
      {references.map((ref) => (
        <li key={ref.key} dangerouslySetInnerHTML={{ __html: ref.html }} />
      ))}
    </ol>
  );
}

Each reference comes out as F. Last, "Title of paper," Journal Name, vol. X, no. Y, pp. Z-Z, Year. (or and/et al. past six authors), following the IEEE Editorial Style Manual: titles of works contained in something else (articles, chapters, conference papers, theses) are quoted, while standalone containers (books, journals, proceedings) are italicized. Unlike ABNT/MLA/APA, formatBibliographyIeee does not alphabetize — IEEE numbers references by citation order, so pass entries already in the order you want them numbered (an <ol> then supplies the [n]).

MLA bibliography formatting

import { parseBibtex, formatBibliographyMla } from 'latex-cite-editor';

const entries = parseBibtex(bibText);
const references = formatBibliographyMla(entries); // sorted alphabetically by author surname

function WorksCited() {
  return (
    <ol>
      {references.map((ref) => (
        <li key={ref.key} dangerouslySetInnerHTML={{ __html: ref.html }} />
      ))}
    </ol>
  );
}

Each reference comes out as Last, First. "Title of Source." Container, vol. X, no. Y, Year, pp. Z-Z. per MLA (9th ed.): Last, First, and First Last. for two authors, collapsing to Last, First, et al. past two; titles of works contained in something else are quoted, standalone containers (books, journals, proceedings) are italicized.

APA bibliography formatting

import { parseBibtex, formatBibliographyApa } from 'latex-cite-editor';

const entries = parseBibtex(bibText);
const references = formatBibliographyApa(entries); // sorted alphabetically by author surname

function References() {
  return (
    <ol>
      {references.map((ref) => (
        <li key={ref.key} dangerouslySetInnerHTML={{ __html: ref.html }} />
      ))}
    </ol>
  );
}

Each reference comes out as Last, F. M. (Year). Title of work. Journal Name, Volume(Issue), pages. per APA (7th ed.): authors are &-joined (Last, F. M., & Last, F. M.) for up to 20, with the 21+-author ellipsis rule past that. APA never quotes or italicizes the title of the work itself — only the container (journal name + volume, or an edited book) is italicized.

Reference-manager export (EndNote, RefMan, RefWorks)

Besides the display styles above, a whole .bib file can be exported as a single plain-text file for importing into a reference manager — the same "Import into: BibTeX / EndNote / RefMan / RefWorks" split Google Scholar's own "Cite" dropdown uses, as opposed to its display styles (MLA, APA, ...).

import { parseBibtex, exportEndNote, exportRefMan, exportRefWorks } from 'latex-cite-editor';

const entries = parseBibtex(bibText);

const endnoteFile = exportEndNote(entries); // .enw tagged format: "%0 Journal Article", "%A Author", ...
const risFile = exportRefMan(entries); // .ris format: "TY  - JOUR", "AU  - Author", ...
const refworksFile = exportRefWorks(entries); // RefWorks tagged format: "RT Journal Article", "A1 Author", ...

// Trigger a browser download for any of them:
function downloadFile(filename: string, content: string) {
  const url = URL.createObjectURL(new Blob([content], { type: 'text/plain' }));
  Object.assign(document.createElement('a'), { href: url, download: filename }).click();
  URL.revokeObjectURL(url);
}

Each function returns the entire bibliography as one string (entries separated by a blank line) — ready to hand to downloadFile() above, or write straight to a .enw/.ris/.txt file.

Combining a style with \cite{} numbering

The functions above (formatBibliographyIeee, formatBibliographyMla, formatBibliographyApa, formatBibliographyAbnt) are for a standalone reference list — MLA/APA/ABNT alphabetize, since that's what those standards require. If you're using \cite{} + resolveCitations instead, pass { style } to it directly (see Quickstart) so the bibliography stays in [1][2][3] citation order — as it must, to match the in-text markers — while each line is formatted per that style:

import { parseBibtex, resolveCitations, formatBibliography, exportBibliography } from 'latex-cite-editor';

const entries = parseBibtex(bibText);
const { bibliographyHtml } = resolveCitations(markdownSource, entries, { style: 'ieee' });

// Or reach for the dispatchers instead of importing each style/format function by name:
const mla = formatBibliography(entries, 'mla'); // same as formatBibliographyMla(entries)
const risFile = exportBibliography(entries, 'refman'); // same as exportRefMan(entries)

See examples/citation-styles.tsx for a live style-switcher + export picker, or the docs site.

API

  • parseBibtex(source: string): BibEntry[] — parses .bib text (handles nested braces in field values, e.g. title = {The {Higgs} Boson}).
  • resolveCitations(text: string, entries: BibEntry[], options?: { style?: CitationStyle }): ResolvedCitations — replaces every \cite{key[,key2]} with numbered, linked markers (ordered by first appearance, like LaTeX + natbib's numeric style) and returns an HTML bibliography block. If an entry has a url, link, or doi field, its bibliography line is wrapped in a link to that address (checked in that order). Pass { style: 'ieee' | 'mla' | 'apa' | 'abnt' } to format each line per that citation style instead of the plain default — the [1][2][3] numbering stays in citation order either way. Each BibliographyItem also gets an html field (the styled line) when style is set.
  • extractCiteKeys(text: string): string[] / formatEntry(entry: BibEntry): string / resolveEntryUrl(entry: BibEntry): string | undefined — lower-level building blocks. formatEntry already runs author/title/journal through cleanLatexText.
  • formatBibliographyAbnt(entries: BibEntry[]): AbntReference[] — formats every entry per ABNT NBR 6023 (see above) and sorts the result alphabetically by author surname (or title, if authorless). Each AbntReference is { key, html, sortKey }, where html is escaped and ready to inject (title wrapped in <strong>).
  • formatEntryAbnt(entry: BibEntry): AbntReference — the single-entry version behind formatBibliographyAbnt, if you want to format/sort a list yourself.
  • formatAuthorsAbnt(rawAuthorField: string): string — just the author-list logic: and-separated BibTeX names into "SOBRENOME, Nome", joined with ; , collapsing to "PRIMEIRO SOBRENOME, Nome et al." past three authors (or when the field contains a bare others).
  • formatBibliographyIeee / formatBibliographyMla / formatBibliographyApa(entries: BibEntry[]): FormattedReference[] (plus matching formatEntryIeee/formatEntryMla/formatEntryApa and formatAuthorsIeee/formatAuthorsMla/formatAuthorsApa) — same shape as the ABNT functions, formatting per IEEE, MLA (9th ed.), or APA (7th ed.) instead. IEEE preserves input order (it numbers by citation order, not alphabetically); MLA and APA alphabetize by author surname like ABNT.
  • formatBibliography(entries: BibEntry[], style: CitationStyle): FormattedReference[] — dispatcher over all four styles, where CitationStyle = 'abnt' | 'ieee' | 'mla' | 'apa'. AbntReference/IeeeReference/MlaReference/ApaReference are all aliases of the shared FormattedReference shape ({ key, html, sortKey }).
  • exportEndNote / exportRefMan / exportRefWorks(entries: BibEntry[]): string — export the whole bibliography as a single plain-text file: EndNote's tagged (.enw) format, RIS (.ris, "RefMan"), or RefWorks' tagged format, for importing into a reference manager rather than displaying on a page.
  • exportBibliography(entries: BibEntry[], format: ExportFormat): string — dispatcher over the three export formats, where ExportFormat = 'endnote' | 'refman' | 'refworks'.
  • cleanLatexText(value: string): string — converts LaTeX accent macros ({\'e}, {\^o}, {\c c}, {\v c}, {\ss}, {\'\i}, ...) and symbol macros (\url{...}, \textordmasculine) as exported by Google Scholar/reference managers into real Unicode (é, ô, ç, č, ß, í, ...), and strips leftover {} grouping braces.
  • escapeHtml(value: string): string — escapes &/</>/", used internally by resolveCitations and the ABNT formatter; exported for building your own HTML output from BibEntry fields.
  • <CiteEditor /> (from latex-cite-editor/react, a 'use client' module) — the editor component. Props: value, onChange, bibEntries, fontSize, placeholder, minHeight, className.
  • CiteEditorHandle (from latex-cite-editor/react, via ref) — imperative API for toolbars: focus(), getSelection(), wrapSelection(before, after, placeholder?), insertAtLineStart(prefix), insertText(text), duplicateCurrentLine(), openSearch(), and the raw CodeMirror view.

<CiteEditor /> lives on a separate /react subpath (with its own 'use client' directive) so that the root entry — parseBibtex, resolveCitations, and friends — stays free of React/CodeMirror and safe to import from a React Server Component (e.g. to resolve citations at render time on the server).

  • citationCompletionSource, latexCommandCompletionSource, latexHighlightPlugin, latexHighlightTheme — exported separately in case you're composing your own CodeMirror extension list instead of using <CiteEditor />.

Recommended host CSS

The rendered bibliography/citation markers use plain classes so you can style them with your own design system:

.citation-ref a { text-decoration: none; }
.citation-ref.citation-missing { color: #ef4444; }
.bibliography ol { list-style: decimal; padding-left: 1.5rem; }
.bibliography li { margin-bottom: 0.5rem; }
.bibliography .citation-link { text-decoration: underline; }
.citation-backref { text-decoration: none; opacity: 0.6; }

Docs site & live examples

https://reinanbr.github.io/latex-cite-editor/

A static site (source in docs-src/) renders the files in examples/ as real, interactive <CiteEditor /> instances — not screenshots — plus the API reference above. It's built with Vite, aliasing the latex-cite-editor/latex-cite-editor/react imports in the example files straight to src/, so the demo always reflects the current code.

npm run docs:dev    # serve the docs site locally with hot reload
npm run docs:build  # build to ./docs (the GitHub Pages source for this repo)

After changing src/ or examples/, run npm run docs:build and commit the updated docs/ folder so the published site stays in sync. (One-time repo setup: Settings → Pages → Deploy from a branch → main / docs.)

License

MIT © Reinan Br