If you've ever needed to embed a code editor inside a React application — for a playground, an admin panel, a low-code tool, a documentation site with runnable examples — you've probably run into the same problem.
Building one from scratch is a substantial undertaking. The feature list alone is daunting: syntax highlighting for multiple languages, line numbers, indentation handling, autocomplete, themes, keyboard shortcuts. Ace Editor is a mature, battle-tested solution to this problem. The react-ace package wraps it for use in React applications. This guide covers how to get it running, how to configure the properties that matter most, and how to handle the events you'll inevitably need.
Key Takeaways
-
react-aceis the React wrapper around Ace Editor — it exposes Ace's functionality through React props rather than direct DOM manipulation. - Syntax highlighting is per-language and requires importing the language mode separately. Same for themes. Neither ships bundled by default.
- The
setOptionsprop is where most configuration lives — autocomplete, line numbers, tab size, print margin. Worth knowing what's in there before trying to work around it. - Event handlers (
onChange,onFocus,onBlur,onCopy,onPaste) cover the interactions you need to respond to. They receive the event and the editor instance. - Performance on large files is generally good, but very large content (tens of thousands of lines) benefits from the
useWorker: falseoption insetOptionsto disable the syntax worker for heavy loads.
Instruction: Render Ace Editor from React Side
Ace Editor is one of the most widely used open-source code editors on the web — it powers the editor in services like AWS Cloud9 and dozens of online code playgrounds. The react-ace package exists specifically to make it work naturally inside a React component tree, managing the editor's lifecycle through React's mounting and unmounting rather than through manual DOM manipulation.
What you get out of the box: syntax highlighting for over 100 languages, a library of themes, bracket matching, code folding, search and replace, multiple cursors, and configurable keyboard bindings including Vim and Emacs modes. It's a lot to get from a single package installation.
Now Let's Install React Ace Editor in Your React App
Before installing, make sure you have a React app set up and Node.js with npm available. If you're starting from scratch:
npx create-react-app my-editor-app
cd my-editor-app
Installing with npm
Install both react-ace and ace-builds — the latter provides the language modes, themes, and other assets:
npm install react-ace ace-builds
Both packages are needed. react-ace is the React component. ace-builds contains the syntax definitions, theme files, and workers that the component loads on demand.
Basic Usage and Configuration Options for Ace Editor
Once installed, import the component and the language mode and theme you need. Here's a minimal working example:
import React from 'react';
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/theme-github';
function ReactAceEditor() {
function onChange(newValue) {
console.log('change', newValue);
}
return (
<AceEditor
mode="javascript"
theme="github"
onChange={onChange}
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
/>
);
}
export default ReactAceEditor;
The mode prop tells Ace which syntax highlighting rules to use. The theme prop sets the visual theme. Both need to be imported separately — if you set mode="javascript" without importing the mode file, you get a plain text editor with no highlighting.
The name prop sets the DOM ID of the editor container. It needs to be unique if you're rendering multiple editors on the same page. Duplicate IDs cause silent conflicts that are annoying to debug.
$blockScrolling: true suppresses a deprecation warning in the console. Include it.
Customizing React Ace Editor
The more complete configuration uses additional props and the setOptions object:
import React from 'react';
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/theme-github';
function MyEditorComponent() {
function onChange(newValue) {
console.log('change', newValue);
}
return (
<AceEditor
mode="javascript"
theme="github"
onChange={onChange}
name="UNIQUE_ID_OF_DIV"
editorProps={{ $blockScrolling: true }}
fontSize={14}
showPrintMargin={true}
showGutter={true}
highlightActiveLine={true}
setOptions={{
enableBasicAutocompletion: false,
enableLiveAutocompletion: false,
enableSnippets: false,
showLineNumbers: true,
tabSize: 2,
}}
/>
);
}
export default MyEditorComponent;
A few props worth explaining:
showGutter controls the line number column on the left. Usually want this on for a code editor. Turn it off only if you're using the editor for something where line numbers don't make sense contextually.
highlightActiveLine highlights the line the cursor is currently on. Standard IDE behavior, generally expected.
fontSize in pixels. 14 is the usual default. If the surrounding UI has a specific font size, matching it matters for visual consistency.
tabSize: 2 sets two-space indentation. Use 4 if your codebase convention requires it. This affects what happens when the user presses Tab, not just how existing content displays.
The autocomplete options (enableBasicAutocompletion, enableLiveAutocompletion) are disabled by default above. To enable them you also need to import the autocomplete extension:
import 'ace-builds/src-noconflict/ext-language_tools';
Without that import, enabling the options does nothing.
Examining Customization Options for Outlook, Themes, and Highlighting Syntax
Ace has language modes for over 100 languages. Each mode provides syntax highlighting, auto-indentation, and in some cases code completion specific to that language. Import whichever you need:
import 'ace-builds/src-noconflict/mode-python';
import 'ace-builds/src-noconflict/mode-html';
import 'ace-builds/src-noconflict/mode-css';
import 'ace-builds/src-noconflict/mode-typescript';
Themes work the same way — import first, then reference by name in the theme prop:
import 'ace-builds/src-noconflict/theme-monokai';
import 'ace-builds/src-noconflict/theme-tomorrow';
import 'ace-builds/src-noconflict/theme-dracula';
Here's an example switching the mode to HTML with the GitHub theme:
import React from 'react';
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-html';
import 'ace-builds/src-noconflict/theme-github';
function ReactAceEditor() {
return (
<AceEditor
mode="html"
theme="github"
name="html-editor"
editorProps={{ $blockScrolling: true }}
/>
);
}
export default ReactAceEditor;
If you're building an interface where users can switch languages or themes, maintain the mode and theme as state and dynamically update the props. Ace handles the transition smoothly — you don't need to remount the editor.
Implementing Custom Key Bindings and Handling Editor Events: Focus, Blur and Transformation
react-ace exposes event handler props for all the interactions you'll need to respond to:
import React from 'react';
import AceEditor from 'react-ace';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/theme-github';
function handleFocus(event, editor) {
console.log('Editor is focused');
}
function handleBlur(event, editor) {
console.log('Editor has lost focus');
}
function onChange(newValue) {
console.log('Content changed to:', newValue);
}
function ReactAceEditor() {
return (
<AceEditor
mode="javascript"
theme="github"
onChange={onChange}
onFocus={handleFocus}
onBlur={handleBlur}
name="event-demo-editor"
editorProps={{ $blockScrolling: true }}
/>
);
}
export default ReactAceEditor;
onChange fires on every content change and receives the full current content as a string. This is the handler you'll use to sync the editor's content with your component state or an upstream store.
onFocus and onBlur both receive the DOM event and the underlying Ace editor instance. The editor instance gives you direct access to Ace's API if you need to do something the React props don't expose — call editor.getValue() to get content, editor.setValue() to set it programmatically, editor.getSession().setUndoManager(new ace.UndoManager()) to reset undo history.
Additional event props: onCopy, onPaste, onSelectionChange, onCursorChange. The last two fire frequently — avoid expensive operations in those handlers.
For custom keyboard shortcuts, use commands:
<AceEditor
commands={[
{
name: 'save',
bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
exec: (editor) => {
handleSave(editor.getValue());
},
},
]}
// ... other props
/>
The exec function receives the editor instance, giving you access to the current content, selection, cursor position, or anything else from the Ace API.
A Few Things Worth Knowing
Controlled vs uncontrolled. react-ace works in both modes. Pass a value prop and manage the content externally (controlled). Leave value unset and let the editor manage it internally (uncontrolled). Controlled mode requires updating the value prop in your onChange handler, otherwise the editor will appear to reset on each keystroke. This is the most common setup mistake.
Height and width. The component renders with explicit dimensions. The default is 500px width and 500px height. Override with the width and height props or with CSS on the container. Using "100%" for both and sizing the container works, but requires the container to have an explicit height — if the parent is auto-height, the editor collapses.
Multiple editors. Each instance needs a unique name prop. The name becomes the DOM element ID. Duplicate names on the same page cause the second editor to reference the first one's underlying instance.
Ace Editor with react-ace is a solid choice for any React application that needs an embedded code editing experience. The feature set is broad, the configuration is flexible, and it handles real-world use cases well — from simple code display to full browser-based IDEs.
The react-ace documentation on GitHub covers the complete prop reference. The Ace Editor documentation covers the underlying editor API for anything the React wrapper doesn't expose directly.
Building React applications that need advanced UI components or custom tooling? Innostax's frontend team has experience across React component libraries, custom editors, and complex interface requirements. Get in touch if you want a practical conversation about your project.
Top comments (0)