rn-code-highlighter
v0.1.2
Published
A native, virtualized syntax highlighter for React Native.
Maintainers
Readme
rn-code-highlighter
A native syntax highlighter and live code editor for React Native. Highlight.js performs tokenization locally, and React Native primitives render the result. It does not use a WebView or download runtime assets.
Install
npm install rn-code-highlighterreact and react-native are peer dependencies.
Usage
import { CodeHighlighter } from 'rn-code-highlighter';
export function SourcePreview() {
return (
<CodeHighlighter
code={'const greeting: string = "Hello";'}
language="typescript"
theme="dark"
showLineNumbers
/>
);
}CodeHighlighter is an alias of CodeViewer; both names are exported. The component is controlled:
when code changes it tokenizes and renders the new value.
Live editor
CodeEditor is a controlled native editor. It uses a multiline TextInput for editing, selection,
the caret, keyboard behavior, and accessibility, with syntax-highlighted text rendered underneath.
import React, { useState } from 'react';
import { CodeEditor } from 'rn-code-highlighter';
export function SourceEditor() {
const [code, setCode] = useState('const answer = 42;');
return (
<CodeEditor
value={code}
onChangeText={setCode}
language="typescript"
theme="dark"
showLineNumbers
/>
);
}The editor supports native selection, two-axis scrolling, line numbers, read-only mode, and standard
TextInput behavior through textInputProps. Syntax highlighting falls back to plain text above
maxHighlightedCharacters, but the editable value is never truncated. It is intended for source
snippets and normal-sized files; use CodeViewer for very large, read-only files because its rows are
virtualized.
Customization
Presentation is split into independent style layers, while native input and scroll behavior can be configured with prop bags:
import React, { useRef, useState } from 'react';
import { CodeEditor, type CodeEditorHandle } from 'rn-code-highlighter';
export function CustomizedEditor() {
const editor = useRef<CodeEditorHandle>(null);
const [code, setCode] = useState('// Start typing');
return (
<CodeEditor
ref={editor}
value={code}
onChangeText={setCode}
language="typescript"
style={{ borderRadius: 12 }}
gutterStyle={{ borderRightWidth: 0 }}
codeStyle={{ letterSpacing: 0.2 }}
lineNumberStyle={{ opacity: 0.7 }}
lineNumberFormatter={(line) => String(line).padStart(3, '0')}
selectionColor="#4c8dff66"
textInputProps={{ accessibilityLabel: 'TypeScript source' }}
verticalScrollViewProps={{ keyboardDismissMode: 'interactive' }}
/>
);
}codeStyle is applied to both the highlighted text and transparent input, keeping their glyphs
aligned. Use fontFamily, fontSize, and lineHeight for typography metrics. If a custom monospace
font has an unusual glyph width, set characterWidth to its measured width. inputStyle is for
input-only presentation and editorContentStyle, lineStyle, gutterStyle, and lineNumberStyle
target their respective layers.
By default, editor changes pass through normalizeCodeInput: curly/full-width quotes become ASCII
code quotes, CRLF becomes LF, and pasted non-breaking spaces become regular spaces. It never trims
ordinary spaces or indentation. Pass a custom inputNormalizer, or null to preserve input exactly.
On iOS, smartInsertDelete defaults to false to prevent the system from adding a space after paste;
it can be re-enabled explicitly.
A CodeEditorHandle ref exposes focus(), blur(), isFocused(), scrollTo(), and the one-based
scrollToLine(). The editor also reports calculated dimensions through onContentSizeChange.
Editor props
| Prop | Type | Default |
| --- | --- | --- |
| value | string | required |
| onChangeText | (value: string) => void | required |
| language | string | plain text |
| theme | "dark" \| "light" \| CodeViewerTheme | "dark" |
| showLineNumbers | boolean | true |
| lineNumberStart | number | 1 |
| lineNumberFormatter | (line: number) => string \| number | String |
| editable | boolean | true |
| fontFamily | string | platform monospace |
| fontSize | number | 14 |
| lineHeight | number | 21 |
| tabSize | number | 2 |
| maxHighlightedCharacters | number | 250000 |
| maxContentWidth | number | 100000 |
| characterWidth | number | estimated from fontSize |
| autoScrollToCaret | boolean | true |
| inputNormalizer | ((nextValue, previousValue) => string) \| null | normalizeCodeInput |
| smartInsertDelete | boolean | false |
| horizontalScrollEnabled | boolean | true |
| verticalScrollEnabled | boolean | true |
| showsHorizontalScrollIndicator | boolean | true |
| showsVerticalScrollIndicator | boolean | true |
| bounces | boolean | false |
| codeStyle, inputStyle, lineNumberStyle | TextStyle | none |
| editorContentStyle, gutterStyle, lineStyle | ViewStyle | none |
| textInputProps | TextInputProps | editor-friendly defaults |
| horizontalScrollViewProps | ScrollViewProps | editor-friendly defaults |
| verticalScrollViewProps | ScrollViewProps | editor-friendly defaults |
| onContentSizeChange | (size) => void | none |
Props
| Prop | Type | Default |
| --- | --- | --- |
| code | string | required |
| language | string | plain text |
| theme | "dark" \| "light" \| CodeViewerTheme | "dark" |
| showLineNumbers | boolean | true |
| selectable | boolean | true |
| fontFamily | string | platform monospace |
| fontSize | number | 11 |
| lineHeight | number | 17 |
| gutterWidth | number | 48 |
| horizontalPadding | number | 10 |
| tabSize | number | 2 |
| maxHighlightedCharacters | number | 250000 |
| maxRenderedCharacters | number | 1000000 |
| truncationMessage | string | built-in message |
The viewer falls back to plain text for unknown languages and inputs larger than
maxHighlightedCharacters. Rendering is truncated at maxRenderedCharacters.
Exports
CodeHighlighter,CodeViewer,CodeEditor, and the default exportcodeViewerThemestokenizeCodeandnormalizeCodeInput- default size limits and all public TypeScript types
Development
npm install
npm test
npm run typecheck
npm run build