Nikx - Custom Frontend Framework

In November and December of 2024, I built a minimal frontend framework called Nikx as part of the Programmeren 3 course. The goal was not to create a usable alternative to existing frameworks, but to deepen my understanding of how tools like React parse and transform JSX-style syntax into executable JavaScript. Nikx is extremely simple by design. It includes its own grammar, a compiler pipeline, and a demo web application. The focus was on learning how to go from source code with mixed syntax (logic plus markup) to generated DOM manipulation code.
Goal
The key objective of this project was to experience what happens "under the hood" in frameworks like React. That included:
Designing a language that mixes HTML-style elements with JavaScript-like logic
Building a lexer and parser using ANTLR
Walking a concrete parse tree to build a clean and usable AST
Writing a custom code generator that emits real JavaScript
Integrating the whole thing into a working Vite-based demo app
Separating responsibilities into clean, modular packages
Unlike React or real-world compilers, Nikx does not include dynamic behavior or reactivity. Instead, it compiles static Nikx code into real DOM-creating JavaScript.
Architecture Overview
Nikx is composed of several key parts, each with a distinct responsibility:
1. Parser and Lexer (ANTLR)
I used ANTLR to generate the parser, lexer, and base visitor from a custom EBNF grammar. The grammar supports constructs like variable declarations, function declarations, nested HTML elements, and function calls. ANTLR took care of parsing source files and producing a parse tree with all the syntactic elements I needed.
2. AST Construction
Instead of using the parse tree directly, I built a custom AST (Abstract Syntax Tree) by extending the ANTLR visitor.
I implemented NikxAstVisitor
, which walks the parse tree and returns simplified AST nodes.
Each language feature is represented by a dedicated node class, including:
ProgramNode
,VariableDeclarationNode
,FunctionDeclarationNode
htmlElementNode
(with support for nested and self-closing tags)ExpressionStatementNode
,FunctionCallNode
,ArgumentListNode
, andLiteralNode
The AST ensures that code generation is predictable and clean.
3. Code Generation
Once the AST is built, I pass it to my code generator: NikxVisitorImpl
.
This visitor converts AST nodes into real JavaScript strings that use document.createElement
, appendChild
, and document.createTextNode
.
I use the nanoid
library to generate unique variable names for each DOM element to avoid conflicts and ensure proper scoping. For example, each <button>
tag becomes something like const _button_AB12CD = document.createElement("button");
.
The generator also:
Adds DOM elements to
document.getElementById("app")
Supports function calls and argument passing
Handles literals, blocks, and variable declarations
Features
Nikx supports a limited but complete set of features:
box
keyword for declaring variablesfun
keyword for declaring functionsString, number, and boolean literals
Function calls with arguments
Nested JSX-like HTML structure
Self-closing and normal HTML elements
Static rendering into the DOM
These features are enough to replicate a basic “Hello World” app, complete with layout, logic, and rendering.
Example
An example Nikx program might look like this:

This will be compiled into valid JavaScript that creates all DOM elements and injects them into the HTML page using native DOM APIs.
Testing
I implemented tests at several levels:
AST construction tests: Ensuring correct node structures from Nikx input
Syntax error detection: Invalid code throws readable errors
End-to-end tests: Validating that full Nikx programs are compiled into working HTML output
For example, calling an undeclared function or mismatching HTML tags throws custom error messages like:
You cannot call function "greet", because it is not declared
Project Structure
The Nikx project is split into three independent directories:
nikx-compiler: ANTLR grammar, AST classes, parser, visitor, and code generator
nikx-vite-plugin: Vite plugin that compiles
.nikx
files during bundlingnikx-application: A demo frontend app that runs compiled Nikx files and renders the output
Everything is written in TypeScript, and each module has its own package.json
.
Build and Run Instructions
You can build everything automatically by running the build-all.bat
script.
Or follow the manual steps:
npm install && npm run generate
innikx-compiler
npm install && npm run build
innikx-vite-plugin
npm install && npm run build
innikx-application
Run the app with
npm run dev
innikx-application
Run all tests with
npm run test
innikx-compiler
What I Learned
This project taught me more about how compilers work than any theory lesson ever could. I learned:
How to define a formal language using EBNF
How ANTLR works and how to generate parse trees
How to build a custom AST and use the visitor pattern
How to translate high-level language features into low-level JavaScript
The structure and limitations of JSX-style syntax
How to separate logic cleanly into compiler, plugin, and application layers
It was the first time I worked this deeply with language parsing and generation. Despite its simplicity, Nikx gave me a complete experience of what building a programming tool feels like.
Result
Nikx is not intended to be used in production, but it is a fully functional mini-framework with its own language, compiler, and runtime. It can parse simple mixed-syntax files, generate JavaScript, and render real HTML output in the browser.
It also gave me the opportunity to apply compiler concepts, build a Vite plugin, write custom AST logic, and manage a multi-project structure in a clean and testable way. View the code of this project on my github