npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

application.ts

v1.0.4

Published

Simplified Web SPA app framework with vanilla JavaScript and CSS

Readme

Application.Ts

A lightweight, dependency-free except template.ts and stackview.ts TypeScript framework for building single-page applications (SPAs) with pure vanilla JavaScript and CSS. No build tools required, just modern web standards.

📺 Live Examples →

What is Application.Ts?

Application.Ts is a minimalist SPA framework that combines:

  • Routing: URL-based navigation with parameters and guards
  • Templating: Reactive data binding with Template.Ts
  • View Management: Stack-based view transitions with StackView.Ts
  • Component Model: Web Components for reusable UI elements

Built on web standards, Application.Ts provides a simple yet powerful foundation for creating modern web applications without the complexity of larger frameworks.

Quick Setup

npm install application.ts
import { App } from 'application.ts';
import { HomeView } from './views/home.view';

const app = new App('#root');

app.router
    .map('/', HomeView)
    .notFound(NotFoundView);

app.start();
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My App</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/main.ts"></script>
  </body>
</html>

Features

🚀 Routing

  • Pattern-based routing with URL parameters
  • Navigation guards (canEnter)
  • Programmatic navigation
  • Browser history support
  • Query string handling
app.router
    .map('/', HomeView)
    .map('/user/:id', UserView)
    .map('/dashboard', DashboardView, {
        canEnter: () => AuthService.isLoggedIn()
    })
    .notFound(NotFoundView);

Route Guards with canEnter

Protect routes with navigation guards. The canEnter function can:

  • Return true to allow navigation
  • Return false to block navigation
  • Return a string path to redirect
// Simple authentication check
app.router.map('/dashboard', DashboardView, {
    canEnter: () => {
        return AuthService.isLoggedIn();
    }
});

// Redirect to login if not authenticated
app.router.map('/profile', ProfileView, {
    canEnter: () => {
        if (!AuthService.isLoggedIn()) {
            return '/login'; // Redirect to login page
        }
        return true; // Allow access
    }
});

// Access route parameters in guard
app.router.map('/admin/:section', AdminView, {
    canEnter: (params) => {
        if (!AuthService.isAdmin()) {
            return '/'; // Redirect to home
        }
        return true;
    }
});

// Async guards for API checks
app.router.map('/document/:id', DocumentView, {
    canEnter: async (params) => {
        const hasAccess = await checkDocumentPermission(params.id);
        return hasAccess ? true : '/unauthorized';
    }
});

🎨 Reactive Templates

Data binding with Template.Ts v2:

  • @on: - Event handlers
  • @prop: - Property binding
  • @att - Attribute binding
  • @batt - Boolean attribute binding
  • @if - Conditional rendering
  • @for - List rendering
  • {{ }} - Expression interpolation
const template = `
<div>
    <h1>{{ title }}</h1>
    <button @on:click="increment">Count: {{ count }}</button>
    <ul>
        <li @for="items">{{ item.name }}</li>
    </ul>
</div>`;

🧩 Component System

Build reusable components with AppView base class:

import { AppView, Register } from 'application.ts';

@Register
export class MyComponent extends AppView {
    template() {
        return `<div>{{ message }}</div>`;
    }

    state() {
        return { message: 'Hello World' };
    }
}

📐 Layouts

Wrap views with shared layouts:

import { DefaultLayout } from './layouts/default.layout';

// Register and set default layout
app.registerLayout('default', DefaultLayout);
app.setDefaultLayout('default');

// Or specify layout per route
app.router
    .map('/', HomeView)
    .map('/about', AboutView, { meta: { layout: 'default' } });

🎯 View Lifecycle

Hook into view lifecycle events:

export class MyView extends AppView {
    async onMounted() {
        // View mounted to DOM
    }

    async stackViewShown() {
        // View became visible
    }

    async stackViewHidden() {
        // View hidden
    }
}

How to Use

1. Create a View

// views/home.view.ts
import { AppView, Register } from 'application.ts';

const template = `
<div class="home">
    <h1>{{ title }}</h1>
    <p>Counter: {{ count }}</p>
    <button @on:click="increment">Increment</button>
</div>`;

class State {
    title: string = 'Home Page';
    count: number = 0;
    
    increment: () => void = () => {
        this.count++;
    };
}

@Register
export class HomeView extends AppView {
    template() {
        return template;
    }

    state() {
        return new State();
    }
}

2. Set Up Routes

// main.ts
import { App } from 'application.ts';
import { HomeView } from './views/home.view';
import { AboutView } from './views/about.view';
import { UserView } from './views/user.view';

const app = new App('#root');

app.router
    .map('/', HomeView)
    .map('/about', AboutView)
    .map('/user/:id', UserView);

app.start();

3. Navigate Between Views

// Programmatic navigation
this.navigate('/about');
this.navigate('/user/123');

// In templates with links
<a href="/about">About</a>
<a href="/user/42">User Profile</a>

4. Access Route Parameters

export class UserView extends AppView {
    async onMounted() {
        const userId = this.params?.id;
        console.log('User ID:', userId);
    }
}

5. Create Components

// components/button.component.ts
import { AppView, Register } from 'application.ts';

const template = `
<button @on:click="handleClick" class="btn">
    {{ label }}
</button>`;

@Register
export class AppButton extends AppView {
    template() { return template; }
    
    state() {
        return {
            label: 'Click me',
            handleClick: () => {
                this.dispatchEvent(new CustomEvent('buttonclick', {
                    bubbles: true
                }));
            }
        };
    }
    
    get label() { return this.viewState.label; }
    set label(value: string) { this.setState('label', value); }
}

Use in templates:

<app-button @prop:label="'Save'" @on:buttonclick="handleSave"></app-button>

Examples

🚀 View Live Examples

Explore the /examples folder for complete working examples:

  • Minimal - The simplest possible app
  • Basic - Routing, layouts, and components
  • Advanced - Full-featured SPA with services, state management, and more

API Reference

App

const app = new App(selector: string, options?: AppOptions);
app.router // Access router
app.start() // Start the application

Router

router.map(path: string, view: typeof AppView, options?: RouteOptions)
router.notFound(view: typeof AppView)
router.navigate(path: string)
router.start()

App

app.registerLayout(handler: string, layoutClass: typeof AppView)
app.setDefaultLayout(handler: string)
app.registerView(handler: string, viewClass: typeof AppView)
app.start()

AppView

abstract class AppView {
    template(): string // Define HTML template
    state() // Define reactive state
    
    // Lifecycle hooks
    onBeforeMount()
    onMounted()
    onBeforeUnmount()
    onUnmounted()
    onStateChanged()
    onParamsChanged()
    
    // Navigation
    navigate(path: string)
    
    // State management
    setState(key: string, value: any)
    setStates(updates: Record<string, any>)
    update() // Force re-render
}

License

MIT

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

Links