smartquotify
v1.1.3
Published
Convert straight quotes to smart/curly quotes — built for legal text, works everywhere
Maintainers
Readme
smartquotify
Convert straight quotes to smart/curly quotes. Zero dependencies. TypeScript-first. Built for legal text, works everywhere.
Lawyers hate straight quotes, but LLMs and most legal tech output them everywhere. smartquotify fixes both display and input.
Install
npm install smartquotifyWhat It Converts
| Input | Output | Rule |
|-------|--------|------|
| "hello" | “hello” | Double quotes |
| 'world' | ‘world’ | Single quotes |
| don't | don’t | Apostrophes |
| '90s | ’90s | Abbreviated years |
| the '604 patent | the ’604 patent | Patent shorthand |
| 6'5" | 6′5″ | Prime marks |
| O'Brien | O’Brien | Names |
| (the "Seller") | (the “Seller”) | Contract terms |
Usage
Core (any JS/TS)
import { smartquotify } from 'smartquotify'
smartquotify('"Don\'t stop"')
// → “Don’t stop”
smartquotify('the \'604 patent')
// → the ’604 patent
smartquotify('6\'2"')
// → 6′2″HTML Mode
Protects tag attributes and comments from conversion. All visible text (including inside <code> and <pre>) gets smart quotes.
smartquotify('<a href="url">"click"</a>', { html: true })
// → <a href="url">"click"</a>
smartquotify('<em>"The court held . . . that"</em>', { html: true })
// → <em>"The court held . . . that"</em>Markdown Mode
Skips fenced code blocks, inline code, and link URLs. Link text still gets converted.
smartquotify('He said "hello" and `"code"` here.', { markdown: true })
// → He said "hello" and `"code"` here.
smartquotify('["click here"](http://example.com)', { markdown: true })
// → ["click here"](http://example.com)Both flags can be combined: { html: true, markdown: true }.
Input Enhancement (any framework)
Attach to any <input>, <textarea>, or contenteditable element. Users type straight quotes, see smart quotes. No cursor jumping.
import { smartquotifyInput } from 'smartquotify'
const textarea = document.querySelector('textarea')
const cleanup = smartquotifyInput(textarea)
// Later, detach:
cleanup()React (smartquotify/react)
import { useSmartQuotify, SmartText } from 'smartquotify/react'
// Hook for controlled inputs
function ChatInput() {
const { value, onChange, ref } = useSmartQuotify('')
return <textarea ref={ref} value={value} onChange={onChange} />
}
// Component for displaying LLM output
function Response({ text }: { text: string }) {
return <SmartText as="p">{text}</SmartText>
}Legal-Specific Edge Cases
This library is specifically designed for legal text. It correctly handles patterns that general-purpose smart quote libraries get wrong:
- Patent shorthand:
the '604 patent→the ’604 patent - Contract defined terms:
(the "Seller")→(the “Seller”) - Em dash context:
the court—"in its discretion"—ruled→the court—“in its discretion”—ruled - Legal ellipses:
"The court held . . . that"→“The court held . . . that” - Bracketed edits:
"[T]he defendant argued"→“[T]he defendant argued” - Measurements in evidence:
the 5'10" suspect→the 5′10″ suspect
API
smartquotify(text, options?)
Pure string transform. Returns a new string with smart quotes.
Options:
primes(boolean, defaulttrue) — Convert measurement marks to prime charactershtml(boolean, defaultfalse) — Skip HTML tag syntax and comments; converts all visible textmarkdown(boolean, defaultfalse) — Skip code blocks, inline code, and link URLs; converts link text
smartquotifyInput(element, options?)
Attaches live smart-quote conversion to an input element. Returns a cleanup function.
Works with <input>, <textarea>, and contenteditable elements.
useSmartQuotify(initialValue?, options?) (React)
Hook for controlled inputs. Returns { value, onChange, ref }.
<SmartText> (React)
Memoized display component. Props:
as— HTML element to render (default:'span')options— smartquotify optionschildren— string to convert
Design decisions
Deterministic, no LLM. Smart quote conversion is fully rule-based — it doesn't need a language model. Every input produces the same output, every time. This makes it safe for real-time input enhancement with zero latency and no API cost.
Legal text first. General-purpose smart quote libraries get legal patterns wrong: patent shorthand ('604), contract defined terms ("Seller"), measurement marks in evidence (6'2"). smartquotify handles these correctly because it was built for legal writing from the start.
Zero dependencies. The core library has no runtime dependencies. The React bindings use React as a peer dependency. Nothing else.
Input enhancement built in. Most smart quote libraries only transform static strings. smartquotify also attaches to live <input>, <textarea>, and contenteditable elements — users type straight quotes and see smart quotes, with no cursor jumping.
Development
npm install
npm test # 74 tests
npm run typecheck
npm run build # ESM + CJS + .d.tsLicense
Apache-2.0
