When it comes to complex web application development on the frontend, we often encounter the need to implement features using vanilla JavaScript—whether it’s for direct DOM manipulation or integrating a specific jQuery plugin. Sometimes, a from-scratch implementation using plain HTML, CSS, and vanilla JavaScript is the best (or only) choice.
What is Still.js
Still.js is a modern Open Source framework that offers the same capabilities as Angular, React, and Vue—without abandoning vanilla JavaScript.
Because of its pure JS approach, Still.js:
- Does not require preprocessing
- Does not depend on bundlers like Webpack or Vite
- Is ideal for teams and developers who prefer direct, no-compromise access to native web technologies in addition to those modern features a Web Framework provides.
Check it up:
Suitable for complex and enterprise applications
Enterprise-grade web applications need more than just rich UI features. They require: modularization
, user permission management
, component routing
, validation
, separation of concern
, communication management
, Micro-frontend architecture
(e.g. Frontend embedding and interaction), and more.
Still.js supports all of this features natively without the burden of a bundler increasing build time and potential complexity or even tooling overhead.
Some code samples
Note: Those will only work within a Still.js project with the correct setup, which is thoroughly explained in the official documentation.
Basic component:
Bellow is a simple component implementing a counter, inspite the template is placed inside the class, it can be moved to a .html file which is appropriate in complex apps.
import { ViewComponent } from "../../../@still/component/super/ViewComponent.js";
export class CounterComponent extends ViewComponent {
isPublic = true;
count = 0;
template = `
<div>
<p>My counter state is @count</p>
<button (click)="increment()">Increment (@count)</button>
</div>
`;
increment() {
this.count = this.count.value + 1;
}
}
Basic User authorization management:
In the bellow implementation we're stating that some components won't be accessible by the user, this can be done with proper business logic according to the user role checking.
import { StillAppMixin } from "./@still/component/super/AppMixin.js";
import { Components } from "./@still/setup/components.js";
import { AppTemplate } from "./app-template.js";
import { CheckingAccount } from "./app/components/BankAccount/CheckingAccount.js";
import { NewAccountForm } from "./app/components/BankAccount/NewAccountForm.js";
import { SavingAccount } from "./app/components/BankAccount/SavingAccount.js";
export class StillAppSetup extends StillAppMixin(Components) {
constructor() {
super();
//Bellow the entry point component is being set
this.setHomeComponent(NewAccountForm);
const blackListComponents = [SavingAccount, CheckingAccount];
//Make components black-listed by passing it to setBlackList App configuration
this.setBlackList(blackListComponents);
}
async init() {
return await AppTemplate.newApp();
}
}
Basic Form validation
Still.js provides built-in validators, but custom ones can be implemented effortlessly.
import { ViewComponent } from "../../../@still/component/super/ViewComponent.js";
export class BasicForm extends ViewComponent {
isPublic = true;
firstName = '';
shoeSize;
template = `
<div>
<form>
<div class="form-group">
<label>Shoe Size</label>
<input
(value)="shoeSize"
(validator)="number"
(validator-warn)="Invalid shoe size, number is required"
placeholder="Enter valid shoe size"
>
</div>
</form>
</div>
`;
}
Global State management:
A service serves for both Global reactive storage and data flow (e.g. HTTP requests) implementations. Services path can be set in the application level, and we can overriden for specific service with @Path
annotation.
import { ViewComponent } from "../../../@still/component/super/ViewComponent.js";
import { CustomersService } from "../../service/api/CustomersService.js";
export class BiddingDisplay extends ViewComponent {
isPublic = true;
/** Service declaration, will get injected automatically due to Inject anottation
* from the specified Path path due to the annotation
* @Inject
* @Path service/api/
* @type { CustomersService } */
custService;
template = `<div></div>`;
/** Component Hook which takes place when it's completly render and startder */
stAfterInit() {
this.custService.customerList.onChange((newValue) => {
console.log('Ths customerList store got updated with: ', newValue);
});
}
}
Hurry up, get yourself started with Still.js
Contribute for the project as you can, check the GitHub repository
Still.js is available through NPM on @stilljs/cli. Watch the Youtube ▶️ playlist and bear for more coming ones.
Watch this Tudo App tutorial on Youtube ▶️, clone or download the provided GitHub Todo app project and run locally. It demonstrates key concepts like component communication
, event and state binding
, reactive updates
, @Proxy
, and component styling
.
Connect and share your feedback on the Discord channel.