
code-input.js is a Web Component for editable syntax-highlighted code fields built around a native <textarea>.
It uses Prism.js, highlight.js, or a custom highlighting callback to handle the syntax coloring while the text field keeps textarea values, selection methods, events, and HTML form behavior.
Features
- Prism.js, highlight.js, and custom highlighting callbacks.
- Native textarea values, selections, validation methods, and input/focus events.
- HTML form fields with progressive fallback markup.
- Direct browser files and ECMAScript module builds.
- Indentation, bracket pairing, autocomplete, find/replace, and go-to-line plugins.
- CSS helpers for autogrow, line wrapping, and Prism line numbers.
- Token-selection callbacks and custom plugin lifecycle hooks.
- Bidirectional text through the HTML
dirattribute.
How To Use It
Installation
Load a syntax highlighter before registering a code-input template.
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/themes/prism.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/components/prism-core.min.js"
data-manual></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/autoloader/prism-autoloader.min.js"></script>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/code-input.min.css">
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/code-input.min.js"></script>
Or install them via NPM:
npm install @webcoder49/code-input prismjs
Basic Usage
Register a template before the <code-input> element initializes. template chooses the registered template, and language sets the syntax language.
Keep <textarea data-code-input-fallback> inside the element when the field needs pre-registration or no-JavaScript fallback behavior.
<script>
codeInput.registerTemplate(
"code-editor",
new codeInput.templates.Prism(Prism)
);
</script>
<code-input
template="code-editor"
language="javascript"
name="source"
placeholder="Enter JavaScript"
>
<textarea
data-code-input-fallback
name="source"
placeholder="Enter JavaScript"
>const total = items.reduce((sum, item) => sum + item.price, 0);</textarea>
</code-input>
ECMAScript Modules
A bundler-based project can import the core, template, plugins, and styles from the npm package:
import codeInput from "@webcoder49/code-input";
import PrismTemplate from "@webcoder49/code-input/templates/prism.mjs";
import Indent from "@webcoder49/code-input/plugins/indent.mjs";
import Prism from "prismjs";
import "@webcoder49/code-input/code-input.css";
import "prismjs/themes/prism.css";
codeInput.registerTemplate(
"code-editor",
new PrismTemplate(Prism, [
new Indent()
])
);
Use highlight.js
Use codeInput.templates.Hljs after loading and configuring highlight.js:
codeInput.registerTemplate( "code-editor", new codeInput.templates.Hljs(hljs) );
Use a Custom Highlighter
codeInput.Template accepts a custom highlighting callback. Set isCode to false when the field edits highlighted text that is not source code.
const todoTemplate = new codeInput.Template(
function (codeElement) {
codeElement.innerHTML = codeElement.innerHTML.replace(
/\b(TODO|FIXME)\b/g,
"<mark>$1</mark>"
);
},
true,
false,
false
);
codeInput.registerTemplate("notes", todoTemplate);
HTML Attributes
template, language, and common textarea attributes can be set directly on <code-input>. Put form-critical attributes on the fallback textarea as well when fallback submission matters.
| Attribute | Description |
|---|---|
template | Selects a template registered with codeInput.registerTemplate(). The first registered template becomes the default. |
language | Sets the highlighting language and the language-* class used by code templates. |
lang | Alternate language attribute used when language is absent. |
value | Sets the current field value. |
placeholder | Sets placeholder text. |
autocomplete | Passed to the internal textarea. |
autocorrect | Passed to the internal textarea. |
autofocus | Passed to the internal textarea. |
cols | Passed to the internal textarea. |
dirname | Passed to the internal textarea. |
disabled | Passed to the internal textarea. |
form | Associates the field with an HTML form. |
maxlength | Sets the maximum text length. |
minlength | Sets the minimum text length. |
name | Sets the submitted form field name. |
readonly | Sets the read-only state. |
required | Applies native textarea required validation when used as an HTML attribute. |
rows | Sets the textarea row count. |
spellcheck | Controls native spell checking. |
wrap | Sets textarea wrapping behavior. |
aria-* | Passed to the internal textarea. |
dir | Controls text direction, including bidirectional text. |
tabindex | The component transfers keyboard focus to its internal textarea. |
JavaScript Properties
Use the HTML required attribute plus checkValidity() or reportValidity() when validation state is important.
| Property | Description |
|---|---|
autocomplete | Gets or sets the autocomplete attribute. |
cols | Gets or sets the textarea column count. |
defaultValue | Gets or sets the initial value used for form reset. |
textContent | Reads or updates the stored initial value. |
dirName | Gets or sets dirname. |
disabled | Gets or sets the disabled state. |
form | Returns the associated form. |
labels | Returns associated labels. |
maxLength | Gets or sets maxlength. |
minLength | Gets or sets minlength. |
name | Gets or sets the form field name. |
placeholder | Gets or sets placeholder text. |
readOnly | Gets or sets the read-only state. |
required | JavaScript required-state accessor. See the 2.9.0 caveat above. |
rows | Gets or sets the row count. |
selectionDirection | Gets or sets selection direction. |
selectionEnd | Gets or sets the selection end index. |
selectionStart | Gets or sets the selection start index. |
textLength | Returns the current value length. |
type | Returns "textarea". |
validationMessage | Returns the native textarea validation message. |
validity | JavaScript validity accessor. See the 2.9.0 caveat above. |
value | Gets or sets the current editor text. |
willValidate | Returns the validation-state flag from the textarea after registration. |
wrap | Gets or sets wrapping behavior. |
JavaScript Methods
| Method | Description |
|---|---|
blur(options) | Calls blur() on the editable or fallback textarea. |
checkValidity() | Runs native form validation. |
focus(options) | Focuses the editable or fallback textarea. |
reportValidity() | Runs validation and asks the browser to report the result. |
setCustomValidity(message) | Sets a custom validation message. |
setRangeText(replacement, start, end, selectMode) | Replaces a text range and schedules a highlighting update. |
setSelectionRange(start, end, direction) | Updates the selection range. |
setKeyboardNavInstructions(instructions, includeAriaDescriptionFirst) | Sets instructions shown during keyboard navigation and can append them to the ARIA description. |
Element References
| Property | Description |
|---|---|
templateObject | Current codeInput.Template after registration. |
textareaElement | Internal editable textarea. |
preElement | <pre> used for highlighted output. |
codeElement | <code> inside the highlighted <pre>. |
dialogContainerElement | Non-scrolling container used by plugin dialogs and keyboard instructions. |
pluginData | Per-element storage object for plugins. |
Events
code-input_load fires after the element, its template, and its plugins finish registration. These textarea events can be listened for on <code-input>:
changeselectionchangeinvalidinputfocusblurfocusinfocusout
const editor = document.querySelector("code-input");
editor.addEventListener("code-input_load", function () {
editor.focus();
});
editor.addEventListener("input", function () {
console.log(editor.value);
});
Template API
| API | Description |
|---|---|
codeInput.registerTemplate(name, template) | Registers a template under a string name. |
new codeInput.Template(highlight, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) | Creates a template around a custom highlighting callback. |
Template.highlight | Highlighting callback used by the template. |
Template.preElementStyled | Selects whether the <pre> or <code> element receives the highlighter’s primary styles. |
Template.isCode | Controls code-language handling and language-* classes. |
Template.includeCodeInputInHighlightFunc | Passes the <code-input> element to the highlighting callback when true. |
Template.plugins | Plugin instances attached to the template. |
new codeInput.templates.Prism(Prism, plugins = [], preElementStyled = true) | Creates a Prism.js template. |
new codeInput.templates.Hljs(hljs, plugins = [], preElementStyled = false) | Creates a highlight.js template. |
codeInput.templates.characterLimit(plugins = []) | Creates the proof-of-concept character-limit template. It reads data-character-limit and optional data-overflow-msg. |
codeInput.templates.rainbowText(colors, delimiter = "", plugins = []) | Creates the proof-of-concept repeating-color template. |
codeInput.templates.prism() | Deprecated Prism.js factory kept for backward compatibility. |
codeInput.templates.hljs() | Deprecated highlight.js factory kept for backward compatibility. |
When rainbowText() receives no color array, its built-in palette is:
[ "red", "orangered", "orange", "goldenrod", "gold", "green", "darkgreen", "navy", "blue", "magenta" ]
JavaScript Editor Plugins
Load a plugin file before constructing its plugin class, then pass the instance to the template.
<script src="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/plugins/indent.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/plugins/auto-close-brackets.min.js"></script> <script> codeInput.registerTemplate( "code-editor", new codeInput.templates.Prism(Prism, [ new codeInput.plugins.Indent(), new codeInput.plugins.AutoCloseBrackets() ]) ); </script>
| Plugin | Public constructor and methods |
|---|---|
AutoCloseBrackets | new codeInput.plugins.AutoCloseBrackets(bracketPairs = {"(": ")", "[": "]", "{": "}", '"': '"'}) |
Autocomplete | new codeInput.plugins.Autocomplete(updatePopupCallback); callback arguments are popup element, textarea, selection end, and optional selection start. |
Autodetect | new codeInput.plugins.Autodetect(); uses highlight.js language detection. |
FindAndReplace | new codeInput.plugins.FindAndReplace(useCtrlF = true, useCtrlH = true, translations = {}, alwaysCtrl = true); showPrompt(codeInputElement, replacePartExpanded) opens the dialog. |
GoToLine | new codeInput.plugins.GoToLine(useCtrlG = true, translations = {}); showPrompt(codeInputElement) opens the dialog. |
Indent | new codeInput.plugins.Indent(defaultSpaces = false, numSpaces = 4, bracketPairs = {"(": ")", "[": "]", "{": "}"}, escTabToChangeFocus = true, translations = {}); the browser build also contains disableTabIndentation() and enableTabIndentation(). |
SelectTokenCallbacks | new codeInput.plugins.SelectTokenCallbacks(callbacks, onlyCaretNotSelection = false, caretAtStartIsSelected = true, caretAtEndIsSelected = true, createSubTokens = false, partiallySelectedTokensAreSelected = true, parentTokensAreSelected = true) |
SelectTokenCallbacks.TokenSelectorCallbacks | new TokenSelectorCallbacks(tokenSelectedCallback, selectChangedCallback); createClassSynchronisation(selectedClass) creates class-sync callbacks. Default class: code-input_select-token-callbacks_selected. |
SpecialChars | new codeInput.plugins.SpecialChars(colorInSpecialChars = false, inheritTextColor = false, specialCharRegExp); currently unstable with other plugins, Unicode characters, or highlight.js. |
Plugin Translation Keys
Plugin dialogs and keyboard instructions accept translation objects.
| Plugin | Translation keys |
|---|---|
FindAndReplace | start, none, oneFound, matchIndex, error, infiniteLoopError, closeDialog, findPlaceholder, findCaseSensitive, findRegExp, replaceTitle, replacePlaceholder, findNext, findPrevious, replaceActionShort, replaceAction, replaceAllActionShort, replaceAllAction |
GoToLine | closeDialog, input, guidanceFormat, guidanceLineRange, guidanceColumnRange, guidanceValidLine, guidanceValidColumn |
Indent | tabForIndentation, tabForNavigation |
Dialog Plugin Requirements
The default Find and Replace dialog needs an editing area at least 500px wide and 170px tall. The Go To Line dialog needs at least 300px by 150px.
Browser keyboard shortcuts can intercept Ctrl/Cmd+F, Ctrl+H, or Ctrl/Cmd+G. Provide a button that calls showPrompt() when the command must stay reachable independently of browser shortcut behavior.
CSS-Only Plugin Helpers
Autogrow
Load the Autogrow stylesheet and use code-input_autogrow_height, code-input_autogrow_width, or both.
Available size variables:
--code-input_autogrow_min-height--code-input_autogrow_max-height--code-input_autogrow_min-width--code-input_autogrow_max-width
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/plugins/autogrow.min.css">
<code-input
class="code-input_autogrow_height"
style="
--code-input_autogrow_min-height: 120px;
--code-input_autogrow_max-height: 320px;
"
template="code-editor"
language="css"
>
<textarea data-code-input-fallback>body {
font-family: system-ui;
}</textarea>
</code-input>
Wrap
Load its stylesheet and apply code-input_wrap.
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/plugins/wrap.min.css">
<code-input
class="code-input_wrap"
template="code-editor"
language="javascript"
>
<textarea data-code-input-fallback></textarea>
</code-input>
Prism Line Numbers
Use Prism’s line-numbers plugin together with code-input’s compatibility stylesheet and the line-numbers class.
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/[email protected]/plugins/line-numbers/prism-line-numbers.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/plugins/line-numbers/prism-line-numbers.min.js"></script>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/WebCoder49/[email protected]/plugins/prism-line-numbers.min.css">
<code-input
class="line-numbers"
template="code-editor"
language="javascript"
>
<textarea data-code-input-fallback></textarea>
</code-input>
Plugin Authoring API
Custom JavaScript plugins extend codeInput.Plugin. Pass the attribute names that the plugin needs to watch to super().
| Hook or member | Description |
|---|---|
constructor(observedAttributes) | Registers attributes watched by the plugin. |
observedAttributes | Stores the watched attribute names. |
beforeHighlight(codeInput) | Runs before highlighting. |
afterHighlight(codeInput) | Runs after highlighting. |
beforeElementsAdded(codeInput) | Runs before the internal editing elements are inserted. |
afterElementsAdded(codeInput) | Runs after the editing elements are ready. |
attributeChanged(codeInput, name, oldValue, newValue) | Runs when a watched attribute changes. |
addTranslations(destination, source) | Replaces matching translation values in an existing translation object. |
Styling and Customization
Keep display: grid on visible <code-input> elements. Version 2 uses that grid for the textarea and highlighted code alignment.
Use the library’s padding variables in place of CSS padding, and include a unit for zero values.
- Use
--paddingfor all sides. - Use
--padding-left,--padding-right,--padding-top, and--padding-bottomfor individual sides. - Override Prism theme backgrounds on the highlighted
<pre>when the theme’s selector wins over the host background. - Override highlight.js backgrounds through
.hljs. - Use
code-input:has(textarea:focus)for focus styles. - Load regular and bold weights when a remote monospace font is paired with a highlighting theme that uses bold tokens.
- Set
colororcaret-coloron<code-input>only when replacing the automatic theme-derived colors. - Keep overlays at least three z-index levels above the
<code-input>host when they need to appear over it. - Exclude
code-input textareafrom global textareamax-widthormax-heightrules. - Change runtime classes through
classList. ReplacingclassNamecan remove classes used by code-input.js.
| CSS variable | Purpose |
|---|---|
--padding | Sets editor padding on all sides. Default: 16px. |
--padding-left | Overrides left padding. |
--padding-right | Overrides right padding. |
--padding-top | Overrides top padding. |
--padding-bottom | Overrides bottom padding. |
HTML Forms and Fallback Textarea
Form-related attributes on <code-input> are passed to the internal textarea after registration. Keep the fallback textarea when the form should also work before registration or when JavaScript is unavailable.
<form action="/save-snippet" method="post">
<code-input
template="code-editor"
language="javascript"
name="snippet"
required
aria-label="JavaScript"
>
<textarea
data-code-input-fallback
name="snippet"
required
aria-label="JavaScript"
></textarea>
</code-input>
<button type="submit">Save</button>
</form>
Single-Page Application Caching
DOM caching can preserve an already initialized <code-input> and restore it in an invalid state. Mark the internal textarea as fallback markup immediately before the framework caches the page.
For Turbo, run this during turbo:before-cache:
document
.querySelectorAll("code-input textarea")
.forEach(function (textarea) {
textarea.setAttribute("data-code-input-fallback", "true");
});
Vue and Nuxt
Vue
Tell the Vue compiler that code-input is a custom element:
vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag === "code-input"
}
}
})
Nuxt
code-input.js uses browser APIs and cannot initialize during server-side rendering. Import the core, template, and plugins from client-side code, and register the template before the component mounts in the browser.
Alternatives & Related Resources
- Tiny Code Editor with Plugins and Syntax Highlighting – Yace
- Cross-browser Code Editor For Browser – codejar
- Minimal Clean Code Editor – iblize
- Highlight Ranges Within HTML Textareas – Lightweight Textarea Highlighter







