Create a Real-Time Math Editor in Next.js with Modern Tools
As I worked on adding a math editor feature to my Next.js application, I wanted to leverage the latest technologies to ensure a seamless and efficient development experience. I chose to use Math.js for mathematical computations, Monaco Editor for a professional-grade interface, and Bun as my runtime and bundler. Here's my journey and a step-by-step guide on how to build this feature quickly using these tools.
Math.js is an extensive math library for JavaScript and Node.js, featuring a flexible expression parser with support for symbolic computation. It supports various data types, including numbers, big numbers, complex numbers, fractions, units, and matrices.
Monaco Editor provides a professional-grade editing experience with features like syntax highlighting, autocompletion, and code folding. It's the same editor that powers Visual Studio Code (VS Code).
Bun is a fast and efficient JavaScript runtime that also serves as a bundler and package manager. It offers near-native performance and supports TypeScript out of the box.
To start, I created a new Next.js project using Bun:
bunx create-next-app@latest
During setup, I chose to use TypeScript and the App Router:
✔ What is your project named? … my-math-editor
✔ Would you like to use TypeScript with this project? … Yes
✔ Would you like to use ESLint with this project? … No
✔ Would you like to use `src/` directory with this project? … Yes
✔ Would you like to use experimental `app/` directory with this project? … Yes
Next, I installed Math.js and Monaco Editor:
bun add mathjs @monaco-editor/react
And for TypeScript definitions:
bun add -d @types/mathjs @types/monaco-editor
I created a Monaco Editor component in my Next.js page:
import Editor from '@monaco-editor/react';
function MonacoEditorComponent() {
return (
<Editor height="200px" language="javascript" theme="vs-dark" />
);
}
export default MonacoEditorComponent;
To evaluate expressions in real-time using Math.js, I captured changes in the editor and passed them to Math.js:
import Editor from '@monaco-editor/react';
import { evaluate } from 'mathjs';
function MonacoEditorComponent() {
const handleEditorChange = (value: string) => {
try {
const result = evaluate(value);
console.log(result); // Display result
} catch (error) {
console.error('Error:', error.message);
}
};
return (
<Editor height="200px" language="javascript" theme="vs-dark" onChange={handleEditorChange} />
);
}
export default MonacoEditorComponent;
To ensure that the expressions entered in the Monaco Editor are valid and provide clear feedback to the user, I implemented validation using Math.js along with Monaco's editor markers:
function handleEditorChange(value: string) {
try {
const result = evaluate(value);
// Display result
} catch (error) {
// Show error message in editor
monaco.editor.setModelMarkers(editor.getModel(), 'mathjs', [
{
severity: monaco.MarkerSeverity.Error,
message: error.message,
startLineNumber: 1,
startColumn: 1,
endLineNumber: 1,
endColumn: 100
}
]);
}
}
This approach ensures that when an invalid mathematical expression is entered, a clear error message is shown within the Monaco Editor, providing immediate feedback to the user.
Math.js generates an Abstract Syntax Tree (AST) when parsing expressions. An AST is a tree representation of the source code, making it easier to analyze and evaluate expressions.
Tokenization: The source code is split into smaller chunks called tokens.
Syntax Analysis: Tokens are parsed into a syntax tree by the parser.
AST Creation: The syntax tree is abstracted to retain only essential structural information.
Easier Evaluation: ASTs simplify the evaluation process by structuring expressions into a hierarchical format.
Improved Error Handling: Errors can be pinpointed more accurately within the tree structure.
Optimization: ASTs allow for optimization techniques like constant folding and dead code elimination.
To better support mathematical notation, I defined a custom language for Monaco:
import * as monaco from 'monaco-editor';
monaco.languages.register({ id: 'mathjs' });
monaco.languages.setMonarchTokensProvider('mathjs', {
tokenizer: {
root: [
[/π|θ|λ/, 'constant'], // Greek letters
[/[\^]/, 'operator'], // Exponents
[/sqrt|sin|cos/, 'function']
]
}
});
I enhanced user experience by adding custom autocomplete suggestions for common math functions:
monaco.languages.registerCompletionItemProvider('mathjs', {
provideCompletionItems: () => ({
suggestions: [
{ label: 'sin', kind: monaco.CompletionItemKind.Function },
{ label: 'π', kind: monaco.CompletionItemKind.Constant }
]
})
});
Bun's performance capabilities were a significant advantage. Its fast bundling and execution ensured that my application remained responsive even with complex computations.
To start the development server with Bun, I ran:
bun run dev
Integrating Math.js with Monaco Editor and using Bun as my runtime and bundler offered a powerful solution for creating an advanced mathematical editing feature in my Next.js application. By leveraging Abstract Syntax Trees and addressing common issues, I built a robust tool that meets the needs of my users. Whether you're developing educational software or engineering calculators, this combination provides a solid foundation for complex mathematical computations.
- Jagadhiswaran devaraj
Join Jagadhiswaran on Peerlist!
Join amazing folks like Jagadhiswaran and thousands of other people in tech.
Create ProfileJoin with Jagadhiswaran’s personal invite link.
0
4
0