bad-words is a great 20-minute solution — until real users arrive. Then it censors "classic" and waves through "@ss" in the same afternoon. Filter8 is the deterministic version that catches the dodge and leaves the innocent word alone.
Wrap Filter8 behind the same call you already make. clean(text) becomes one request that returns a verdict and cleaned text — the rest of your app doesn't move.
It's free, offline, zero-dependency, and adds no latency. For a prototype, an internal tool, or a place where a slipped word or a false-flag is a shrug, it's genuinely the right call — ship it. Filter8 is for the moment "good enough" stops being good enough: when a false positive is a support ticket, an evasion is a screenshot, and contact-info leaks are a business problem.
Every team graduates off the wordlist eventually. Skip the support tickets — 100 free calls, no card.