Skip to Content
PackagesCitations & Bibliography

Citations and bibliography

Inline citations, a bibliography that follows them, CSL formatting, and a host adapter for lookups, the user’s library and attachments.

PackageContents
@stesura/bibliographyPlugins, commands, the citeproc formatter, bundled CSL styles and locales, validation, BibTeX / RIS / CSL-JSON importers. Framework-free.
@stesura/bibliography-reactcreateBibliographyUiExtension: the References-tab group, the sources panel, the citation picker, the source dialog, node views.

The citation and bibliography nodes, the bibliography doc attr and the adapter contract are part of core, so a host can replace the plugin and keep the documents.

Setup

import { bibliographyPlugins } from "@stesura/bibliography"; import { createBibliographyUiExtension, withBundledStyles } from "@stesura/bibliography-react"; import type { BibliographyAdapter } from "@stesura/core/types"; // Your library integration (lookup, searchLibrary, enrich, ...); every method is optional. declare const host: Partial<BibliographyAdapter>; // One adapter for both: the plugin formats with it, the UI looks up and searches with it. const adapter = withBundledStyles(host); const uiExtensions = [createBibliographyUiExtension({ adapter })]; const extraPlugins = () => [...bibliographyPlugins({ adapter })];

withBundledStyles fills loadStyle / loadLocale / listStyles with the bundled CSL files (APA, MLA, Chicago author-date and notes, IEEE, Harvard Cite Them Right, NLM/Vancouver; 17 locales), each fetched as its own chunk. citeproc-js (~1 MB) also loads on first use.

Data model

  • Sources live in the document, as CSL-JSON in doc.attrs.bibliography ({ sources, styleId, locale }, locale: null = the document language). The adapter is for the user’s library, never for what the document cites.
  • citation is an inline atom: items ({ sourceId, locator?, label?, prefix?, suffix?, suppressAuthor?, authorOnly? }[]), mode (parenthetical | narrative), and the cached rendering text and missing flag the plugin writes.
  • bibliography is a block atom, allowed only directly in a section’s content (not in cells, notes or headers): scope (document | section), includeUncited, types, and the cached entries the plugin writes. Exports and read-only views render from that cache.
  • Citations work in footnotes and endnotes, ordered at their note’s anchor. In headers and footers they are flattened to text, as in Word.

The plugin heals on every change: sources carried by a pasted citation are registered (a clashing id is renamed and the citation re-pointed), missing is kept current, and rendering runs debounced and out of history, so undo never steps through a re-render.

Commands

CommandDoes
insertCitation({ items, mode?, sources? })Replaces the selection with a citation. sources carries new records; works in note sub-editors.
insertCitationAsFootnote(input)A footnote whose text is the citation (note styles).
editCitation({ pos?, items?, mode?, sources? })Changes the selected citation.
insertBibliography({ scope?, includeUncited?, types? })Inserts a bibliography at the caret.
setBibliographyStyle({ styleId, locale? })Changes the CSL style.
addSource / addSources / updateSource / removeSource / mergeSourcesEdit the sources; merging re-points citations.
convertBibliographyToStatic(pos?)Replaces a bibliography with paragraphs in the Bibliography style.
goToSource(sourceId)Selects the bibliography listing it.

flushBibliography(view) resolves once pending formatting is in the document (tests, exports).

Adapter behaviour

Every optional method the adapter lacks hides its affordance.

MethodUsed byExpected behaviour
loadStyle(id), loadLocale(lang)formatterCSL XML. Required; withBundledStyles supplies them.
lookup(input)pickerDOI, ISBN, arXiv, PMID, URL or free text to { item, confidence }[]. The picker runs it at once for identifiers (classifyInput), on request for free text.
searchLibrary(query)pickerThe user’s library. Set libraryRef on results to enable sync.
subscribeLibrary(refs, cb) / fetchLibraryItems(refs)panelReport changed records; the panel offers to apply them.
enrich(item)panel, citationsSourceSignals: retractions above all (shown on the citation too), corrections, counts, open access. Never a composite score.
resolveAttachmentUrl(ref) / uploadAttachment(file)panelAttachment blobs.

A reference implementation lives in the testbed app (apps/testbed/src/lib/bibliography), not in a package:

  • Lookup ladder (server-side): DOI content negotiation at doi.org (Crossref, DataCite, mEDRA answer CSL-JSON); ISBN through Open Library; arXiv’s API (preferring the published DOI); PubMed / PMC through NCBI’s citation exporter; a URL through its Highwire / Dublin Core / JSON-LD / Open Graph metadata, fetched behind a public-address guard; a PDF through the DOI in its metadata or first pages, then GROBID when GROBID_URL is set; free text through Crossref’s bibliographic query, then a model as the last rung. Model output is re-checked against Crossref; what it alone filled is marked provenance.unverified and flagged in the source form.
  • Enrichment, cached a week per DOI: Crossref update notices (retractions, corrections), Semantic Scholar counts, Unpaywall’s open-access copy (UNPAYWALL_EMAIL).
  • Zotero Web API (ZOTERO_LIBRARY, ZOTERO_API_KEY): search, fetch by key, and change polling on the library version.

DOCX

Citations export as Word CITATION fields in a citation content control, with the cached text as the field result, the sources as a b:Sources custom XML part (Manage Sources in Word), and the bibliography as a BIBLIOGRAPHY field in its content control with the cached entries as its result. A second custom XML part keeps the full CSL-JSON for a lossless round trip. Imports read Word citations, and Zotero and Mendeley citation fields.

Last updated on