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 🙏

© 2025 – Pkg Stats / Ryan Hefner

canoejs

v0.6.5

Published

A lightweight, widget-based UI framework

Readme

CanoeJS 🚦 - Ultra Fast & Lightweight

CanoeJS is an ultra-fast and lightweight UI framework for building dynamic web applications using a Widget-based approach. Inspired by Flutter's layout system, it features an optimized DOM diffing algorithm for fast and efficient rendering.

⚡ Performance Optimizations

🚀 High-Performance Features

  • Ultra-Fast Hash Function: Replaced SHA256 with a simple and efficient hash function
  • Intelligent DOM Diffing: Optimized algorithm that only updates elements that actually changed
  • Event Delegation: Delegated event system for better performance
  • Virtual Scrolling: Support for large lists with virtual rendering
  • Lazy Loading: Deferred loading of widgets to improve initial load time
  • Memoization: Intelligent caching system to avoid repeated calculations
  • Batch Updates: Batch updates to reduce re-renders
  • Render Caching: Render caching to avoid unnecessary re-renders
  • Optimized Router: Route system with cache and efficient search

📊 Performance Comparison

| Framework | Bundle Size | Load Time | Re-rendering | Memory | |-----------|-------------|-----------|--------------|--------| | React | ~42KB | ~150ms | Complete | High | | CanoeJS | ~8KB | ~50ms | Intelligent | Low |


🆕 New Features in v0.6.0

🌍 Global State Management

import { Canoe } from "canoejs";

// Get current state
const currentState = Canoe.getState();

// Update state - triggers automatic re-render
Canoe.setState({ 
  counter: 5, 
  user: { name: "John", age: 25 } 
});

// Nested state updates
Canoe.setState({ 
  user: { 
    ...currentState.user, 
    age: 26 
  } 
});

// Force re-render when needed
Canoe.forceRender();

// Debug state changes
Canoe.debug = true;
Canoe.debugRender({ counter: 10 });

🔄 Automatic Widget Updates

import { Widget, Button, Canoe } from "canoejs";

// Widgets automatically update when their state dependencies change
class CounterWidget extends Widget {
  render(): HTMLElement {
    // System automatically detects this widget uses 'counter' from global state
    const currentCounter = Canoe.getState().counter || 0;
    
    return new Button({
      text: `Counter: ${currentCounter}`,
      callbacks: { 
        click: () => {
          Canoe.setState({ counter: currentCounter + 1 });
        }
      }
    }).render();
  }
}

// Widget automatically updates when 'counter' changes!
const widget = new CounterWidget({});

🎨 Advanced Component System

import { Component } from "canoejs";

class MyComponent extends Component {
  constructor() {
    super({}, {
      onMount: () => console.log('Component mounted!'),
      onUnmount: () => console.log('Component unmounted!'),
      onUpdate: (prevProps, newProps) => console.log('Props updated!'),
      shouldUpdate: (prevProps, newProps) => prevProps.value !== newProps.value
    });
  }

  render(): HTMLElement {
    return new Button({ text: "Click me!" }).render();
  }
}

🌈 Dynamic Theme System

import { ThemeProvider, defaultTheme, darkTheme } from "canoejs";

// Switch themes dynamically
ThemeProvider.setTheme(darkTheme);
ThemeProvider.toggleDarkMode();

// Custom theme
const customTheme = {
  ...defaultTheme,
  colors: {
    ...defaultTheme.colors,
    primary: '#ff6b6b',
    secondary: '#4ecdc4'
  }
};
ThemeProvider.setTheme(customTheme);

🎭 Animation System

import { AnimationManager } from "canoejs";

// Basic animations
await AnimationManager.fadeIn(element);
await AnimationManager.bounce(element);
await AnimationManager.shake(element);
await AnimationManager.rotate(element, 360);

// Custom animations
await AnimationManager.animate(element, [
  { transform: 'scale(0)', opacity: '0' },
  { transform: 'scale(1)', opacity: '1' }
], { duration: 500, easing: 'ease-out' });

// Stagger animations
await AnimationManager.stagger(elements, AnimationManager.fadeIn, 100);

🍞 Toast Notifications

import { ToastManager } from "canoejs";

// Show different types of toasts
ToastManager.success("Operation completed successfully!");
ToastManager.error("Something went wrong!");
ToastManager.warning("Please check your input");
ToastManager.info("Here's some information");

// Custom toast
ToastManager.show({
  title: "Custom Toast",
  message: "This is a custom toast message",
  type: "success",
  duration: 3000,
  position: "top-center"
});

🪟 Modal System

import { Modal } from "canoejs";

const modal = new Modal({
  title: "Confirmation",
  content: new P({ text: "Are you sure?" }),
  size: 'md',
  showCloseButton: true,
  closeOnOverlayClick: true,
  callbacks: {
    onClose: () => console.log('Modal closed')
  }
});

💡 Tooltip System

import { Tooltip } from "canoejs";

const tooltip = new Tooltip({
  text: "This is a helpful tooltip",
  position: 'top',
  trigger: 'hover'
});

// Attach to element
tooltip.attachTo(element);

🛠️ Getting Started

# Install CanoeJS globally (works with npm, bun, yarn, or even Deno).
npm install -g canoejs

# Create a new project
canoejs new <project-name>

# Navigate into your new project
cd <project-name>

# Install dependencies
npm install

# Run the project (with optimized build)
npm run dev

🚧 Creating an App

import { Canoe, Router, PerformanceManager } from "canoejs";

// Configure performance optimizations
PerformanceManager.setConfig({
    enableVirtualScrolling: true,
    enableLazyLoading: true,
    enableMemoization: true,
    enableEventDelegation: true,
    enableBatchUpdates: true,
    enableRenderCaching: true
});

let context = {
  userData: {
    id: "user1",
    name: "John",
    lastName: "Doe",
    email: "[email protected]"
  }
};

Router.addRoute("/", HomePage, "CanoeJS - Home Page");
Router.addRoute("/docs", DocsPage, "CanoeJS - Documentation");

Canoe.buildApp("root", context, Router.render).render();

Example HomePage Widget with Optimizations

import {
  Col, FlexAlignContent, FlexAlignItems, FlexJustify, FlexWrap,
  H, Link, Router, Row, memo, VirtualList, LazyWidget
} from "canoejs";
import Logo from "../widgets/Logo";

const HomePage = () => {
  // Use memoization to avoid unnecessary re-renders
  const memoizedContent = memo("homepage-content", () => [
    new Logo({}),
    new H({ size: 1, text: "CanoeJS" }),
    new H({ size: 3, text: "A lightweight framework for building web applications" })
  ]);

  return new Col({
    css: {
      width: "100%",
      height: "100%",
      background: "linear-gradient(to top, rgba(135, 135, 250, 0.05), rgba(25, 25, 180, 0.1))",
    },
    flexAlignContent: FlexAlignContent.CENTER,
    flexAlignItems: FlexAlignItems.CENTER,
    flexJustify: FlexJustify.CENTER,
    flexWrap: FlexWrap.NOWRAP,
    children: [
      ...memoizedContent,
      new Row({
        flexAlignContent: FlexAlignContent.CENTER,
        flexAlignItems: FlexAlignItems.CENTER,
        flexJustify: FlexJustify.CENTER,
        flexWrap: FlexWrap.NOWRAP,
        children: [
          new Link({ text: "GitHub", to: "https://github.com/jotalevi/CanoeJs" }),
          new H({ size: 5, text: "|" }),
          new Link({ text: "Documentation", to: "/docs" }),
          new H({ size: 5, text: "|" }),
          new Link({ text: "Examples", to: "/examples" }),
        ],
      }),
    ],
  });
};

export default HomePage;

📌 Optimized Widgets

Basic Widgets

Alert Badge Button Card Col Container
GroupedButtons H Input InputGroup InputLabel
Link P Progress Row Spinner

High-Performance Widgets

VirtualList LazyWidget

Advanced Widgets (New!)

Modal Tooltip Toast

VirtualList - For Large Lists

import { VirtualList, H, P } from "canoejs";

const LargeList = () => {
  const items = Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    title: `Item ${i}`,
    description: `Description for item ${i}`
  }));

  return new VirtualList({
    items,
    itemHeight: 60,
    containerHeight: 400,
    renderItem: (item) => new Col({
      children: [
        new H({ size: 4, text: item.title }),
        new P({ text: item.description })
      ]
    })
  });
};

LazyWidget - Deferred Loading

import { LazyWidget, Spinner } from "canoejs";

const LazyComponent = () => {
  return new LazyWidget({
    loader: () => import('./HeavyComponent').then(m => m.default),
    placeholder: new Spinner({})
  });
};

🔄 Optimized Lifecycle Hooks

onLoad

Triggered after the first call to:

Canoe.buildApp("root", context, Router.render).render();

preBuild

Called right after a state update via Canoe.setState().

postBuild

Runs after every state update and allows full access to the current DOM.

Example Usage with Performance Monitoring

import { Canoe, PerformanceManager } from "canoejs";

Canoe.onLoad(() => {
  console.log("App loaded!");
});

Canoe.preBuild(() => {
  PerformanceManager.measureTime("build", () => {
    // Build logic here
  });
});

Canoe.postBuild(() => {
  console.log("Build completed!");
  console.log("Average build time:", PerformanceManager.getAverageTime("build"));
});

🎨 CSS Custom Properties

CanoeJS automatically applies CSS custom properties for theming:

:root {
  --color-primary: #3b82f6;
  --color-secondary: #6b7280;
  --color-success: #10b981;
  --color-danger: #ef4444;
  --color-warning: #f59e0b;
  --color-info: #06b6d4;
  
  --font-family: system-ui, -apple-system, sans-serif;
  --font-size-xs: 0.75rem;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --font-size-xl: 1.25rem;
  
  --spacing-xs: 0.25rem;
  --spacing-sm: 0.5rem;
  --spacing-md: 1rem;
  --spacing-lg: 1.5rem;
  --spacing-xl: 2rem;
  
  --radius-sm: 0.125rem;
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
  --radius-xl: 0.75rem;
  
  --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
  --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}

🚀 Advanced Example

Check out the advanced example in template/src/example-advanced.ts to see all new features in action!

import AdvancedComponent from "./example-advanced";

// Add to your routes
Router.addRoute("/advanced", () => new AdvancedComponent(), "Advanced Demo");

📦 Bundle Size

  • Core Framework: ~8KB gzipped
  • With All Features: ~12KB gzipped
  • Tree-shakeable: Only import what you need

🤝 Contributing

We welcome contributions! Please see our Contributing Guide for details.


📄 License

MIT License - see LICENSE file for details.


🙏 Acknowledgments

  • Inspired by Flutter's widget system
  • Performance optimizations inspired by React and Vue
  • Animation system inspired by Framer Motion
  • Hooks system inspired by React Hooks

Made with ❤️ by the CanoeJS Team