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

@threew/view

v1.0.3

Published

Production-ready View Engine and Template Rendering System for Threew ecosystem

Readme

@threew/view

Introduction

@threew/view is the official View Engine and Template Rendering System for the Threew ecosystem. Designed to be modular, extensible, and lightweight, it provides robust template rendering capabilities with excellent type safety. It supports multiple popular template engines and can be used independently or seamlessly integrated with @threew/app.

This package focuses exclusively on view rendering, deliberately avoiding HTTP frameworks, routers, servers, or middleware engines to maintain a clear and focused scope.

Features

  • Modular & Extensible: Easily integrate new template engines via a simple adapter interface.
  • Lightweight: Minimal dependencies and focused functionality.
  • Type Safety: Built with TypeScript, ensuring a robust and predictable development experience.
  • Multi-Engine Support: Out-of-the-box support for EJS, Handlebars, Nunjucks, and Pug.
  • Caching: Optional in-memory caching for improved performance by avoiding redundant file system reads.
  • Flexible Template Resolution: Supports relative and absolute paths, nested folders, layouts, and partials.
  • Independent or Integrated: Usable as a standalone library or within the Threew application framework.

Installation

To install @threew/view, use your preferred package manager:

npm install @threew/view
# or
yarn add @threew/view
# or
pnpm add @threew/view

Quick Start

Here's a quick example to get you started:

import { ViewManager, EjsAdapter } from '@threew/view'
import path from 'node:path'

async function bootstrap() {
  const views = new ViewManager({
    views: path.join(__dirname, 'views'), // Your templates directory
    cache: true // Enable caching
  })

  // Register an EJS adapter
  views.register('ejs', new EjsAdapter())

  // Render a template
  const html = await views.render('home', {
    title: 'Welcome to Threew View',
    message: 'This is a sample page.'
  })

  console.log(html)
}

bootstrap().catch(console.error)

Engine Registration

@threew/view allows you to register multiple template engines and switch between them. Each engine must implement the ViewEngine interface.

import { ViewManager, EjsAdapter, PugAdapter, HandlebarsAdapter, NunjucksAdapter } from '@threew/view'

const views = new ViewManager()

views.register('ejs', new EjsAdapter())
views.register('pug', new PugAdapter())
views.register('handlebars', new HandlebarsAdapter())
views.register('nunjucks', new NunjucksAdapter())

// Set the default engine
views.setEngine('ejs')

// Check if an engine is registered
console.log(views.hasEngine('pug')) // true

// Get a registered engine
const ejsEngine = views.getEngine('ejs')

// Remove an engine
views.removeEngine('nunjucks')

// Clear all engines
views.clearEngines()

Rendering

The ViewManager provides a simple render method to process your templates.

import { ViewManager, EjsAdapter } from '@threew/view'
import path from 'node:path'

const views = new ViewManager({
  views: path.join(__dirname, 'views')
})
views.register('ejs', new EjsAdapter())

// Assuming 'views/dashboard.ejs' exists
const userData = { user: { name: 'John Doe', role: 'Admin' } }
const dashboardHtml = await views.render('dashboard', userData)
console.log(dashboardHtml)

Layouts and Partials

The ViewResolver and ViewRenderer components are designed to support layouts and partials, allowing for reusable template structures. While the core render method handles basic template resolution, advanced layout and partial handling will depend on the specific capabilities of the registered ViewEngine adapters.

For example, an EJS template might include a partial like this:

<!-- views/layout.ejs -->
<!DOCTYPE html>
<html>
<head>
  <title><%= title %></title>
</head>
<body>
  <%- body %>
  <%- include('partials/footer') %>
</body>
</html>

And a partial:

<!-- views/partials/footer.ejs -->
<footer>
  <p>&copy; 2023 Threew</p>
</footer>

Cache

Enable in-memory caching to prevent repeated file system reads for templates. This can significantly improve performance in production environments.

import { ViewManager, EjsAdapter } from '@threew/view'

const views = new ViewManager({
  cache: true // Enable caching
})
views.register('ejs', new EjsAdapter())

// Templates will be cached after the first render
await views.render('home', { title: 'Cached Page' })

EJS Example

// examples/ejs.ts
import { ViewManager, EjsAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

async function run() {
  const views = new ViewManager({
    views: path.join(__dirname, '../tests/views'),
    cache: true
  })

  views.register('ejs', new EjsAdapter())

  const html = await views.render('home', {
    title: 'Hello from EJS'
  })

  console.log('Rendered HTML:', html)
}

run().catch(console.error)

Handlebars Example

// examples/handlebars.ts
import { ViewManager, HandlebarsAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

async function run() {
  const views = new ViewManager({
    views: path.join(__dirname, '../tests/views'),
    cache: true
  })

  views.register('handlebars', new HandlebarsAdapter())

  // Assuming a template 'views/welcome.hbs' with content '<h1>Hello, {{name}}!</h1>'
  const html = await views.render('welcome', {
    name: 'Handlebars User'
  })

  console.log('Rendered HTML:', html)
}

run().catch(console.error)

Nunjucks Example

// examples/nunjucks.ts
import { ViewManager, NunjucksAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

async function run() {
  const views = new ViewManager({
    views: path.join(__dirname, '../tests/views'),
    cache: true
  })

  views.register('nunjucks', new NunjucksAdapter())

  // Assuming a template 'views/greeting.njk' with content '<h1>Hello, {{ name }}!</h1>'
  const html = await views.render('greeting', {
    name: 'Nunjucks User'
  })

  console.log('Rendered HTML:', html)
}

run().catch(console.error)

Pug Example

// examples/pug.ts
import { ViewManager, PugAdapter } from '@threew/view'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

async function run() {
  const views = new ViewManager({
    views: path.join(__dirname, '../tests/views'),
    cache: true
  })

  views.register('pug', new PugAdapter())

  // Assuming a template 'views/profile.pug' with content 'h1= user.name'
  const html = await views.render('profile', {
    user: { name: 'Pug User' }
  })

  console.log('Rendered HTML:', html)
}

run().catch(console.error)