Written by Rahul Chhodde✏️
Nue JS is a new JavaScript framework that positions itself as a compelling and faster alternative to established frontend JavaScript tools like React, Vue, and Svelte. Although still under development, it has already started gaining attention for its innovative development approach.
Like Svelte, Nue does not use a virtual DOM and instead compiles components into highly optimized imperative code that directly manipulates the DOM. This results in faster rendering, especially for complex applications.
This article will look at Nue as an alternative to Svelte, which is a mature and established JavaScript framework for creating performant JavaScript applications. We will cover:
- What is Nue JS?
- Similarities and differences between Nue JS and Svelte
- Advantages specific to Nue JS
- Getting started with Nue JS
- Nue JS component types
- Event handling in Nue JS
- Control flow utilities in Nue JS
- Styling Nue JS with CSS
- Upcoming Nue tools
We’ll go through some code examples to explore Nue fully. You can check out the code on GitHub and fork the project to explore it on your own.
What is Nue JS?
Tero Piirainen, the creator of Nue as well as the component-based UI library Riot.js, was dissatisfied with the complexity and excessive bloat of modern frontend frameworks. This drove him to introduce a fresh approach emphasizing simplicity, performance, and an improved developer experience.
Nue follows a minimalist approach and aims to offer improved solutions for creating web apps and conventional websites. Without hooks, effects, and props in the traditional sense, Nue leaves it up to the developer to decide how they prefer to extend the framework to add different functionalities.
Its syntax is essentially HTML, where you can also use Nue-only variables, directives, attributes, and modifiers to change how things should be rendered.
Since the majority of the development work in Nue revolves around HTML, CSS, and JavaScript, you may not need additional expertise beyond that. This makes its learning curve shorter and helps developers quickly adapt to the framework.
Combined with the advanced features of NueKit in the future, Nue aspires to position itself as a convincing alternative to popular tools like Vite, Next.js, and Astro.
In the following sections, we will delve deeper into Nue, exploring its similarities and differences with Svelte, its advantages, and, most importantly, the basics of its syntax and a few additional technical aspects.
Similarities and differences between Nue and Svelte
When choosing a JavaScript framework for your project, it is essential to understand the similarities and differences between the options. This will help inform your decision so that you select the framework that best suits your project's requirements and goals.
While Nue and Svelte share the common goal of empowering web UI development, they also share certain fundamental characteristics, as evident from their codebases and code examples.
The below similarities also offer a glimpse into Svelte and its nature if you are not already familiar with it. Let's explore these shared traits:
- Declarative UI construction: Both Nue and Svelte embrace a declarative approach to UI development. This means that you specify how you want your UI to appear, and the framework takes care of rendering it. This streamlined approach simplifies the creation of complex UIs without the need for excessive code
- Unidirectional data flow: Svelte and Nue both adhere to the unidirectional data flow paradigm. This means that data flows from the model to the view but not the other way around. This unidirectional data flow enhances code comprehensibility and facilitates debugging
- Compiler-driven code optimization: The code you write in both Nue and Svelte undergoes a transformation into WebAssembly and pure JavaScript respectively before execution. This compilation process results in significant performance enhancements, especially in scenarios involving complex UIs
- Lightweight and speedy performance: Both frameworks prioritize lightweight and rapid operation, making them an excellent choice for developing high-performance web applications
- Component-centric architecture: Nue and Svelte adopt a component-based architecture for structuring applications. This approach involves constructing UIs from reusable components and streamlining the development and maintenance of complex UIs
- TypeScript-free core: Notably, both Svelte and Nue embrace a TypeScript-free core to simplify their ecosystems. They support dynamic typing within JavaScript, with Svelte utilizing JS Doc for type declarations
Meanwhile, here are some of the significant differences between Nue and Svelte summarized in a table for easier viewing:
Nue | Svelte | |
---|---|---|
Build size | An exceptionally lightweight framework, with a minified and gzipped size of just 2.3kB. Since it’s in its early days of development, you can expect it to grow in size eventually. | Impressively small, with a minified and gzipped size of 2.6kB, making it nearly as compact as Nue, even though it has been around for a few years now. |
Syntax | Employs a straightforward and intuitive syntax, resembling embedded HTML, CSS, and JavaScript. This approach simplifies the development process and enhances code readability. | Uses a template syntax that allows you to seamlessly integrate HTML, CSS, and JavaScript directly into your components. It offers a simple yet powerful way to create dynamic and interactive interfaces. |
Code compilation | Compiles to WebAssembly, which makes it a bit more efficient than Svelte, utilizing a streamlined virtual DOM. | Compiles to vanilla JavaScript at build time, eliminating virtual DOM overhead. It employs a distinct reactive model and offers a beginner-friendly learning curve. |
Ecosystem | Nue is relatively new to the scene, and its ecosystem is smaller than Svelte's. However, the Nue community is expected to expand further, and developers will eventually gain access to Nue-optimized libraries and tools tailored to their needs. | Svelte has a more mature ecosystem, enriched with a wide array of libraries and tools, making it a well-established choice for various web development projects. |
State management | Uses a reactive state system like Svelte, but takes a more declarative approach. Instead of manually managing the state, you declare what needs tracking, letting Nue.js handle automatic UI updates. This approach offers flexibility but requires upfront planning and implementation. | Uses reactive programming to manage the state, so state changes are automatically reflected in the UI without needing explicit updates. Svelte also provides a number of built-in features for managing state, such as observables and stores. |
Build tools | Uses Bun's native bundler by default, but gives you the flexibility to use a different or fallback bundler, such as ESBuild. We'll demonstrate this later in the article. | Uses Vite for code bundling |
Svelte’s minimal footprint coupled with its larger community positions it as an excellent choice for optimizing web applications. Meanwhile, Nue offers a simple, flexible development experience with the option to customize or extend its capabilities as needed.
Advantages specific to Nue JS
Nue claims to offer some advantages that are missing with other prominent JavaScript frameworks and significantly enhance frontend development scalability for the following four reasons:
- Enhanced separation of concerns: Nue promotes a clear separation of concerns, making it easier to scale your project. Clean, easy-to-read code is more conducive to scalability than complex, tangled “spaghetti” code
- Minimalistic approach: Nue’s minimalistic design philosophy encourages succinct code. With just a hundred lines of code, you can achieve what might require a thousand lines in other frameworks. This minimalism simplifies project management and streamlines the scaling process
- Optimal skill alignment: Nue allows your development team to specialize effectively. With Nue’s decoupled styles, UI designers and UX developers can focus on the frontend’s visual aspects, while JavaScript and TypeScript developers can concentrate on the backend. This specialization fosters efficiency and scalability in the development process
- Faster page loads: Nue’s decoupled styling makes isolating primary CSS from secondary elements easy, allowing you to ensure that your HTML page stays within the critical 14kB limit. This optimization results in faster page loading times
Keep in mind, though, that Nue is still being actively developed. While this means we can expect more improvements to the framework itself as well as new advancements in its ecosystem, this may also lead to occasional bugs and broken features.
Getting started with Nue JS
Nue strongly emphasizes its compatibility with Bun, a new JavaScript runtime. However, it can also be configured to work in a Node.js environment. In this section, I will lead you through the setup process of Nue on Node.js, running your first Nue app, and exploring its components basics.
Installing Nue
There is currently no Nue JS CLI available. You can install it in one of two ways. The first option is creating a new Nue project from scratch and adding the Nue JS core package. Otherwise, you can follow the recommended approach and clone the starter project provided by Nue, which includes examples.
Note that as we mentioned earlier, Nue is currently in its early development stages. Given this, it's expected to have some bugs, such as the one I encountered in version 0.1.1 where a core function requires a slight adjustment to work properly.
Let's follow the recommended approach and clone Nue’s official create-nue
starter project:
git clone https://github.com/nuejs/create-nue.git
Next, navigate to the create-nue
directory and install the necessary dependencies using your preferred package manager. In this example, we'll use pnpm:
cd create-nue
pnpm install
You should see something like the below: The install
command will ensure that all required dependencies are in place before you run your Nue project for the first time. If you are not using Bun, which provides built-in bundling, you can install esbuild to minify and bundle your files.
Running your Nue project
After installation, you can use the following command to run the Nue JS project you just obtained and installed from GitHub:
pnpm run start
You should see the following: As shown in the screenshot above, esbuild is installed in the project. Since we're not using Bun, it's automatically configured as the fallback bundler for minifying the build files, as specified in the scripts/minify.js
file. The folder structure will be explained in more detail in the next section.
Exploring the folder structure
When you open the installed project, you will see the folder structure as shown below:
.
└── create-nue/
├── node_modules
├── scripts
├── src
├── www
├── package.json
├── ...
├── ...
└──
It’s a bit different from Svelte and its other counterparts. The most important directories to focus on are scripts
, src
, and www
:
- The
scripts
directory contains various build scripts that automate tasks such as building, compiling, packaging, and preparing a Nue JS application for deployment - The
src
directory, as the name implies, contains all the source files, including components, layouts, and data - The
www
directory contains the final build and includes all files generated by the Nue JS core using resources from thesrc
directory
It is important to note that the names of these directories are not strictly standardized and can be modified and reconfigured by editing the package.json
and script files. This flexibility is relatively uncommon in other frameworks like React, Vue, and Svelte.
Writing your first Nue component
Creating Nue components differs significantly from Svelte and other frameworks. To create a basic component, assign a name attribute prefixed with @
to the parent element within the component structure and save it with your preferred name.
Nue components can be saved with either a .nue
or .htm
extension. Files with .htm
extensions are automatically highlighted in the code editor, but for the files with .nue
extension, you'll need the Nue Language Support extension in VS Code for code highlighting.
As demonstrated below, you can make an HTML attribute dynamic by prefixing it with a colon, allowing it to accept variable properties:
<-- components/logo.nue -->
<div @name="logo">
<a :href="href">
<img class="logo" :src="img_src" />
</a>
</div>
Here's how you use this component and provide variable properties to it.
<logo
img_src="img/logo.svg"
href="//blog.logrocket.com/"
/>
I've simplified the Nue JS starter project for you, and it includes all the examples we will cover in the upcoming sections. You can fork it from here if you'd like.
Nue JS component types
In the previous section, you learned how to create a basic, universal, static Nue component. It's important to note that Nue components can come in various types, each tailored to different structures and use cases.
Nue categorizes these component types into three distinct categories: server components, reactive components, and isomorphic components. Their names are quite self-explanatory, but let’s explore each in detail below.
Server components
Server components are used to render static HTML on the server side without state management or event handlers. You would typically employ them when you want to serve content directly from the server to the client — in other words, when you want to implement server-side rendering (SSR).
You can define server components dynamically in the scripts/render.js
file, render them, and then add them to the final HTML for a given page:
// scripts/render.js
import { render } from 'nuejs-core';
// Define a component for a product card
const serverComponent = `
<div @name="server-component">
<h3>{componentTitle}</h3>
<p>{componentDesc}</p>
</div>
`;
// Render the product card component with some product data
const serverComponentHtml = render(serverComponent, {
title: '...',
desc: '...',
});
console.info(serverComponentHtml);
The following screenshots illustrate a server component's markup being logged on the server console. This data cannot be logged in the client console for clear reasons: Alternatively, you can place any components you want to render on the server in a single file and then fetch them in the scripts/render.js
file.
You can also organize the components in different files and utilize the renderFile
function from the Nue JS core to render them, and subsequently output the rendered result as needed:
// scripts/render.js
async function renderServerComponent(url, ops) {
try {
// Render the product card component with some product data
const serverComponentHtml = await renderFile(url, ops)
return serverComponentHtml
} catch (error) {
console.error(error)
return '' // or handle the error as needed
}
}
export default async function () {
...
let logoComponentHtml = await renderServerComponent(
'./src/components/logo.nue',
{
href: '#',
img_src: 'img/logo.svg',
}
)
console.log(logoComponentHtml)
...
}
The above example shows how to create a utility function that takes a component's file path and some arguments to help construct the component on the server side.
For more details, you can locate the entire scripts/render.js
file here, which encompasses not only rendering the components but also rendering the entire page and injecting data and critical styles into it using the Nue JS core and Node functions.
Reactive components
Reactive components play a key role in client-side rendering (CSR). They can support state, reactivity, and event handling, making them suitable for creating interactive elements.
Reactive components in Nue, also known as islands, can be defined within the HTML structure. In our sample Nue project, the reactive components are defined in the islands.nue
file and compiled in the scripts/compile.js
file.
Here's an example of a reactive Nue component:
<!-- Reactive Component -->
<div @name="reactive-component">
<p>The below button count clicks on it.</p>
<button @click="index++">Click me { index }</button>
<script>
index = 0
</script>
</div>
As mentioned earlier, you have the option to either place all the reactive components in one file — in our case, islands.nue
— and then process it in scripts/compile.js
, or you can organize each reactive component separately and compile them individually, which I think is a bit tedious:
// scripts/compile.js
import { compileFile } from 'nuejs-core';
export default async function (to = 'www/islands.js') {
await compileFile('src/components/reactive-component.nue', to);
console.info('compiled', to);
}
All this compiled code will land right into the www/islands.js
file, which can be enqueued in the index.html
and other markup files in the www
directory.
Isomorphic components
Isomorphic components in Nue come in two flavors: universal and hybrid. Universal components can be rendered on both the server and client, while hybrid components combine server-side and client-side rendering.
The component we previously learned to create was universal in nature. You can revisit it for reference.
You can think of hybrid components as having two sides: one for the server to ensure content is visible without JavaScript, and another for the client to add interactive features.
The Nue JS Docs explain hybrid components using the example of a video player on a website. The server part ensures the video is accessible, which is crucial for search engines and those who prefer a basic experience. When JavaScript is available, the client part takes over, making the video interactive with play and pause buttons.
Nue smoothly blends these two sides, with a Nue “island" acting as a placeholder for the client part in the generated HTML. When it's time for interactivity, the island gets filled with client code and data, making the web experience both accessible and dynamic.
Event handling in Nue JS
Event handling in Nue is similar to event handling in Svelte and Vue, offering various event modifiers to control behavior. However, Nue has its own unique syntax and usage, which we will explore in this section to enable you to create dynamic and interactive Nue components.
To handle events in Nue, you use the @
directive followed by the event type. In the next few sections, we will delve into different event types, providing a more detailed understanding of their usage and syntax.
Inline handlers
Inline handlers are a straightforward way to handle events directly on an attribute. They are ideal for simple expressions, such as:
<button @click="alert('Hello!')">Click me!</button>
In this example, clicking the button triggers the alert()
function with the message Hello!
. We have already observed a similar example earlier in the section on Reactive components.
Method handlers
For more complex functionality and the ability to handle arguments, you can use method handlers. First, create a function in your component's script, and then bind it to the event attribute:
<button @click="greet('John Doe')">Click me!</button>
<script>
function greet(name) {
alert(`Hello, ${name}!`);
}
</script>
This configuration calls the greet()
function with the argument John Doe
when the button is clicked.
Event modifiers
Event modifiers provide shortcuts for common DOM event manipulation tasks. For example, the :preventDefault
modifier prevents the default behavior of the event:
<a
href="#"
@click:preventDefault="console.log('Link clicked!')"
>Click me!</a>
This logs Link clicked!
to the console upon clicking the link without navigating to the href
value.
Key modifiers
Key modifiers bind an event handler to a specific keyboard key. Consider the :enter
modifier, which associates the event handler with the Enter
key:
<input @keyup:enter="search()">
This calls the search()
function when the user presses Enter
while typing in the input field.
Handling multiple events
You can manage multiple events on the same element by separating them with commas. For example:
<button
@click:preventDefault="greet('John Doe')"
@mouseover="console.log('Button hovered!')"
>Click me!</button>
This configuration calls the greet()
function with the argument John Doe
upon clicking the button and logs Button hovered!
to the console when hovering over the button.
Control flow utilities in Nue JS
Like Svelte and other JavaScript frameworks, Nue provides conditional directives and loops to control content rendering and iterate through data structures. Let's explore these control flow utilities with some examples.
Conditional directives
Conditional directives allow you to render content based on specific conditions. The primary conditional directive in Nue is :if
. It accepts an expression as its argument, and the content enclosed within it will only be rendered if the expression evaluates to true
.
For instance, consider the following code snippet. It will display the <h1>
element only if the isLoggedIn
variable is true
:
<h1 :if="isLoggedIn">Welcome, user!</h1>
To handle the case when the expression evaluates to false,
you can use the :else
directive. Here's an example:
<h1 :if="isLoggedIn">Welcome, user!</h1>
<p :else>Please log in.</p>
You can also use the :elif
directive to cater to multiple conditions. The following code snippet renders the <h1>
element if isLoggedIn
is true
, the <h2>
element if isAdmin
is true
, and the <p>
element if both conditions are false
:
<h1 :if="isLoggedIn">Welcome, user!</h1>
<h2 :elif="isAdmin">Welcome, admin!</h2>
<p :else>Please log in.</p>
Loops
Loops are essential for iterating over arrays, objects, and components. The most commonly used loop directive in Nue is :for
. This directive takes an expression as its argument, and its content is rendered for each item in the array, object, or component.
Here's an example that renders a <li>
element for each item in the items
array:
<ul>
<li :for="item in items">{ item }</li>
</ul>
To keep track of each item's state in the loop, you can use the :key
directive. This is especially useful when working with lists of items. In the following code, a unique key is specified for each item in the loop using the id
property:
<ul>
<li :for="item in items" :key="item.id">{ item }</li>
</ul>
Conditional directives and loops together
For more complex logic, combine conditional directives and loops. For instance, the following example renders a <li>
element for each item in the items
array, but only if the item.isImportant
property is true
:
<ul>
<li :for="item in items" :if="item.isImportant">{ item }</li>
</ul>
Nesting conditional directives and loops generate a <ul>
element for each item in the items
array, with <li>
elements created for each item in the item.subItems
array within the :for
directive.
<ul>
<li :for="item in items">
<ul>
<li :for="subItem in item.subItems">{ subItem }</li>
</ul>
</li>
</ul>
By combining conditional directives and loops, you can construct intricate and dynamic user interfaces tailored to your application's needs.
Styling Nue JS with CSS
Nue advocates for using external stylesheets to follow the software design principle of separation of concerns. It does not support using style tags or CSS-in-JS at the component level, emphasizing the importance of maintaining this separation.
Maintaining separate CSS files is consistent with Nue's approach and is also a best practice in general. This practice keeps each CSS file dedicated to its specific task, making management and maintenance significantly easier.
If you examine the scripts/render.js
file, you will notice that the framework recommends reading critical CSS and adding its contents to the content.data
file to use later in the page as inline styles:
// scripts/render.js
export default async function() {
...
const primary_css = await read('primary.css', 'www/css')
const data = yaml.load(await read('content.data'))
data.primary_css = primary_css.replace(/\s+/g, ' ')
...
}
You can now utilize the primary_css
property in a layout file to import the primary CSS data and place it within style tags as inline CSS styles. This approach ensures that the initial UI loads without blocking anything, and the rest of the component styles can be added as needed.
While you can use inline CSS styles within components, it is not a recommended styling method as it can complicate the maintenance process.
Upcoming Nue tools
Nue is about to introduce several noteworthy features, starting with Nue CSS, which revitalizes cascaded styling, and Nue UI, a user-friendly UI library.
Eventually, Nue plans to roll out additional tools, including:
- Nue MVC, which simplifies the development of scalable applications with the MVC architecture
- Nuemark, which enhances Markdown capabilities
- NueKit, which streamlines development similar to SvelteKit, Next.js, and Nuxt.js
With these planned features, it’s evident that Nue has the potential for growth and can offer better solutions to frontend development challenges in the future.
Conclusion
While Nue is still in its early development stages, it shows promise as a powerful framework. However, it's not enough to label Nue as the fastest framework solely based on the fewer lines of code used to create some components than other frameworks.
Working with Nue will require a substantial amount of manual work, including configuring and optimizing build scripts to tailor them to the unique requirements of each project. Furthermore, Nue currently lacks a straightforward method for organizing components, a command-line interface (CLI), and fundamental features like hot-reloading. These issues may be tackled in future releases, particularly with the introduction of NueKit.
On the other hand, Svelte is a more mature framework than Nue and is already performing exceptionally well with a lot of developer-friendly features. I personally find it excellent for creating advanced as well as lightweight JavaScript apps.
If you're interested in exploring a new JavaScript framework with promising features, Nue might be an excellent choice for you. If you prefer a well-established framework with a larger community, a great developer experience, and widespread adoption, then Svelte is the way to go.
I recommend trying both frameworks to determine which one aligns with your preferences. Using Nue for more applications can significantly contribute to identifying issues and fostering its ongoing development and growth.
Are you adding new JS libraries to improve performance or build new features? What if they’re doing the opposite?
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — Start monitoring for free.
Top comments (3)
Very nice!
Thanks for your post. Well.. I want to translate this post into Korean.
Do you mind if I translate your post?
Just what you need to replace the over-complicated Next.js