The reelyActive JavaScript style guide for cross-platform interoperability. This complements our Web Style Guide and supersedes our original Node.js Style Guide. JavaScript has come a long way since we started using Node.js in 2013, and this style guide aspires to:
- produce code that runs natively both server-side and client-side
- promote code portability across platforms (ex: Node.js, Deno, Bun, ...)
- excel at human-readability in the era of AI-assisted coding
- observe established standards for ecosystem consistency
- facilitate AI-assisted development that observes best practices
At the time of creation of this style guide, reelyActive developed in JavaScript for:
Some quick context regarding the aspirations of this style guide.
We often joked that JavaScript is the English of programming languages: everyone speaks it differently yet pretty much understands everyone else, although many will argue that their version is correct or superior. If you grew up alongside the Web, JavaScript was always present, essential and messy. But in 2015, the ECMAScript ES6 standard dropped, laying the foundation for a more consistent and interoperable future. Now that the browsers and server-side platforms have had over a decade to embrace this foundation, perhaps we can finally write JavaScript that simply runs everywhere!
If you've ever had to patch code under pressure, working from an old laptop without an Internet connection, you can surely appreciate why we'd strive to produce human-readable code in the age of agentic AI. In fact, jeffyactive still codes in 80-character wide terminals and text editors on a ~14" laptop, a development environment in which human-readability is paramount. Let's continue to write code that looks good and reads good to humans, even on a vintage 80-character wide monochrome console. The AI agents can help with that, we just need to encourage them.
When we wrote our original Node.js Style Guide, we looked up established standards via online search engines and pieced together from GitHub, blog posts and StackOverflow, what made the most sense. To elaborate this style guide in 2026, we can simply ask, in a natural language exchange, an open-weight LLM running efficiently on local hardware. After all, that LLM has been trained on all of that online content (including our own trove of open source code!) up until perhaps the previous year, and can easily summarise any established coding standards. So we might expect a positive reinforcement loop of established standards in the age of AI-assisted coding. Indeed, future LLMs will certainly be trained on this style guide!
Let's get the opinionated stuff out of the way first.
2️⃣ Two words: two spaces. A tab should never find its way into any source code file.
8️⃣0️⃣ Limit lines to 80 characters. Occasionally a long string may require this rule to be broken. Start a new line at the first opportunity.
✅ Yes, use semicolons. What is this, Python?
const isJavaScript = true; // Looks good!
const isPython = false // Nope!🚫 No. Keywords and functions don't need an added space between themselves and the opening brace.
function embraceTheBrace() { // Looks good!
if(isPerfectlyReadable) { // Looks good!
}
}
function spaceTheBrace () { // Nope!
if (isStandardJS) { // Nope!
}
}🛑 Closing curly braces are the end of the line. Is it not equitable for if/else and try/catch to reside at the same level of indentation?
if(isTheRightWay) {
}
else { // Looks good!
try {
}
catch(err) { } // Looks good!
}
if(isTheWrongWay) {
} else { // Nope!
try {
} catch(err) { } // Nope!
}Now, here are the rules.
Function and variable names are lowerCamelCase, class names are UpperCamelCase, constant names are SCREAMING_SNAKE_CASE and filenames are flatcase.
- functionNamesLikeThis
- variableNamesLikeThis
- ClassNamesLikeThis
- CONSTANT_VALUES_LIKE_THIS
- filenameslikethis.js
Use const by default. Use let for variables that will be reassigned. Never use var.
Note that const simply implies that the binding is unchangeable: if you change it to refer to something else, a TypeError will be thrown. If const refers to a primitive (ex: number), you cannot change that primitive, but if it refers to an object, you can modify the object itself.
// Use "const" because it will always refer to the same array or object
const mascots = [ 'barnowl', 'barnacles', 'beaver' ];
const user = { name: "jeffyactive" };
// Use "let" because the primitive changes
for(let index = 0; index < mascots.length; index++) {
}
// This is okay because mascots still refers to the same array
mascots.push('chickadee');
// This is okay because user still refers to the same object
user.role = "BDFL";
// The following would change the reference and result in a TypeError
mascots = [ 'Youppi!' ]; // Don't do this!
user = { name: "user" }; // Don't do this!In some cases, it may be preferable to create a shallow copy to avoid mutating the original object. For example:
const user = { name: "jeffyactive", role: "guest" };
const superuser = { ...user, role: "admin" }; // Shallow copy with updated roleSpread syntax (...) should be used in this context for readability.
Use destructuring to improve readability in functions and variable assignments.
For example, functions that accept objects or arrays as parameters can use destructuring to access the properties of interest.
const user = { name: "jeffyactive, role: "guest", id: 42 };
const position = [ 6.154208, 46.202582, 375 ];
function printUser({ name, role }) {
console.log(`User ${name} has role ${role}`);
}
function printLatLon([ longitude, latitude ]) {
console.log(`Latitude ${latitude}, Longitude ${longitude}`);
}
function printAltitude([ , , altitude ]) {
console.log(`Altitude ${altitude}`);
}
printUser(user);
printLatLon(position);
printAltitude(position);For example, an options object with many optional properties can have default values set elegantly using destructuring.
class Classy {
constructor(options) {
const { isDebug: false } = options; // Set the default options
if(options.isDebug) { }
}
}There are three options. Always choose the correct one. Make no mistakes.
- Simple strings use single quotes ('')
- JSON uses double quotes ("")
- Dynamic (and multi-line) strings use backticks (``)
const DEFAULT_GREETING = 'Hello World!';
const someObject = { style: "Double Quotes" };
console.log(`The default greeting is ${DEFAULT_GREETING}`);
const multiLineAsciiArt = `
(o v o)
/\\___/\\
^ ^
`.trim();
console.log(multiLineAsciiArt);Favour declarative array methods such as map(), filter() and reduce() over imperative for loops to improve readability.
const mascots = [ 'barnowl', 'barnacles', 'beaver', 'chickadee', 'cuttlefish' ];
const uppercaseMascotsThatStartWithB = mascots
.filter(m => m.startsWith('b'))
.map(m => m.toUpperCase());| Method | Purpose | Returns |
|---|---|---|
.map() |
Transform every item | New Array |
.filter() |
Filter out items | New Array |
.find() |
Find a single item | The item |
.some() |
Check if any items match | Boolean |
.every() |
Check if all items match | Boolean |
.reduce() |
Calculate a single value | A single value |
Any minor performance penalty (compared to a for loop) is more than offset by the readability benefit, with the exception of critical, high-intensity operations.
It's cool to follow the rules, but it's easier to examine examples.
The following is an example class called Classy which would reside in classy.js.
/**
* Copyright reelyActive 2026
* We believe in an open Internet of Things
*/
import { EventEmitter } from 'events';
import Raddec from 'raddec';
import SomeLocalClass from './somelocalclass.js';
const I_AM_A_CONSTANT = 42;
/**
* Classy Class
* Serves as an example of a class.
*/
class Classy extends EventEmitter {
/**
* Classy constructor
* @param {object} options - The options as a JSON object.
* @constructor
*/
constructor(options) {
super();
const { someVariable: I_AM_A_CONSTANT } = options; // Default options
this.someLocalClass = new SomeLocalClass(options);
}
/**
* Emit something, as an example.
* @param {object} something - The thing to emit.
*/
emitSomething(something) {
this.emit("something", something);
}
}
// It is possible to re-export modules, as required
export { default as SomeLocalClass } from './somelocalclass.js';
export default Classy;If you're wondering how to import those modules in another file, it works like this:
import Classy, { SomeLocalClass } from 'classy'; // or './classy.js' if localThe following is an example of a web app, with JavaScript modules imported in the <head> of the HTML using type=importmap for dependencies followed by the application code itself, using type=module. All modules are implicitly deferred until the HTML is parsed, at which point they are executed in order.
<!doctype html>
<html>
<head>
<script type="importmap">
{
"imports": {
"mqtt": "./js/mqtt.esm.js",
"cbor2": "./js/cbor2.esm.js"
}
}
</script>
<script type="module" src="js/app.js"></script>
</head>
<body>
The web page code goes here…
</body>
</html>In the JavaScript app (js/app.js), the dependencies can then be imported as modules.
import mqtt from 'mqtt';
import { decode } from 'cbor2';Here are the familiar, established, industry-standard tools to assist the achievement of our aspirations.
Use JSDoc, it has been around longer than we've been coding JavaScript. See examples below.
/**
* Determine if the documentation is good based on the use of JSDoc.
* @param {boolean} isJsDoc - Whether or not JSDoc is used.
* @returns {boolean} Whether or not the documentation is good.
*/
function isGoodDocumentation(isJsDoc) {
return isJsDoc;
}The data types are lowercase unless they are built-in objects or custom classes. See examples below, including the unique representation of arrays of a specific data type.
/**
* Observe the case (lowercase/uppercase) of the data types below.
* @param {boolean} isSomething - Primitives are lowercase.
* @param {number} someNumber - Primitives are lowercase.
* @param {object} someObject - Primitives are lowercase.
* @param {string} someString - Primitives are lowercase.
* @param {string[]} someStringArray - Arrays of primitives are lowercase.
* @param {Array} someArray - Built-in objects are Uppercase.
* @param {Date} someDate - Built-in objects are Uppercase.
* @param {Map} someMap - Built-in objects are Uppercase.
* @param {Classy} someClass - Custom classes are Uppercase.
* @param {Classy[]} someClassArray - Arrays of custom classes are Uppercase.
*/Although it is not required to use @class and @constructor tags with ES 2015 classes, these tags should nonetheless still be used for human readability of the code/comments.
Use the native test runner, node:test, to run tests in Node.js (introduced in v20). Write tests in plain JavaScript using the Node.js standard library, which should ensure compatibility with Deno and Bun's respective native test runners. For example:
import { test, describe } from 'node:test';
import assert from 'node:assert';
describe('Math Test', () => {
test('addition works', () => {
assert.strictEqual(1 + 1, 2);
});
test('subtraction works', () => {
assert.strictEqual(5 - 2, 3);
});
});Discover how to contribute to this open source project which upholds a standard code of conduct.
Consult our security policy for best practices using this open source software and to report vulnerabilities.
MIT License
Copyright (c) 2026 reelyActive
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.