latex-cite-editor
v0.4.2
Published
A CodeMirror 6 based React editor with LaTeX-style \cite{} bibliography autocomplete backed by BibTeX (.bib) files
Maintainers
Readme
Table of contents
- Why
- Installation
- Quickstart
- ABNT bibliography formatting
- IEEE bibliography formatting
- MLA bibliography formatting
- APA bibliography formatting
- Reference-manager export (EndNote, RefMan, RefWorks)
- Combining a style with
\cite{}numbering - API
- Recommended host CSS
- Docs site & live examples
- License
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-editorRequires 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.bibtext (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 aurl,link, ordoifield, 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. EachBibliographyItemalso gets anhtmlfield (the styled line) whenstyleis set.extractCiteKeys(text: string): string[]/formatEntry(entry: BibEntry): string/resolveEntryUrl(entry: BibEntry): string | undefined— lower-level building blocks.formatEntryalready runs author/title/journal throughcleanLatexText.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). EachAbntReferenceis{ key, html, sortKey }, wherehtmlis escaped and ready to inject (title wrapped in<strong>).formatEntryAbnt(entry: BibEntry): AbntReference— the single-entry version behindformatBibliographyAbnt, 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 bareothers).formatBibliographyIeee/formatBibliographyMla/formatBibliographyApa(entries: BibEntry[]): FormattedReference[](plus matchingformatEntryIeee/formatEntryMla/formatEntryApaandformatAuthorsIeee/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, whereCitationStyle = 'abnt' | 'ieee' | 'mla' | 'apa'.AbntReference/IeeeReference/MlaReference/ApaReferenceare all aliases of the sharedFormattedReferenceshape ({ 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, whereExportFormat = '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 byresolveCitationsand the ABNT formatter; exported for building your own HTML output fromBibEntryfields.<CiteEditor />(fromlatex-cite-editor/react, a'use client'module) — the editor component. Props:value,onChange,bibEntries,fontSize,placeholder,minHeight,className.CiteEditorHandle(fromlatex-cite-editor/react, viaref) — imperative API for toolbars:focus(),getSelection(),wrapSelection(before, after, placeholder?),insertAtLineStart(prefix),insertText(text),duplicateCurrentLine(),openSearch(), and the raw CodeMirrorview.
<CiteEditor />lives on a separate/reactsubpath (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
