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 🙏

© 2024 – Pkg Stats / Ryan Hefner

tsx-express

v0.9.2

Published

Use React TSX views with Express

Downloads

190

Readme

React Views for Express

A simple React view engine for Express

Background

I prefer old school approach to websites, where the server is responsible for state management and serves up HTML content with a sprinkling of interactivity added on the front end. I also like type safety, intellisense, etc. More recently I've been using HTMX and looking for a new view engine. I've used Handlebars, ejs, etc. but none really fit the bill.

My main design goals were :

  • Similar approach as HBS / EJS
  • Specify a default / global master layout
  • Be able to override this at the route level
  • Pass a view at the route level
  • Automatically expose locals to the view
  • Pass custom properties to the view
  • Strongly typed where possible

Enter tsx-express, a simple React view engine for express.

DISCLAIMER - I am not a professional developer, this is a side/weekend project.

Contexts and other features are not implemented to keep this as a simple view engine, similar to other view engines.

Getting started

Setup is pretty easy.

You'll need express, react and tsx-express.

npm i tsx-express

App setup

Setup express as normal and use initializeReactRenderer helper method in your main application file passing in your express app and optional ReactGlobalOptions options. ReactGlobalOptions allows you to specify both a default and child layout.

import express from 'express';
import { initializeReactRenderer } from 'tsx-express';
import Layout1 from "../views/layouts/Layout1"

const app = express();

initializeReactRenderer(app, {defaultLayout:Layout1});

Route setup

In your express routes you then call the renderReact method, passing in your view component, any properties you want to pass to the view, and optionally response options (ReactResponseOptions).

import MyView from "../views/MyView"

app.get('/', (req, res, next) => {
      const options : ReactResponseOptions = {}
      const properties : any = {}
      res.renderReact(MyView, properties, options )
})

You'll notice that your properties are strongly typed according to your view.

typed renderReact method

If you want to override the default layout or child layout at the route level you can pass in ReactResponseOptions.

import Layout2 from "../views/layouts/Layout2"
import MyView from "../views/MyView"

app.get('/', (req, res, next) => {
      const properties : any = {}
      res.renderReact(MyView, properties, {layout:Layout2} );
})

If you want to remove a layout at the route level you can use the NoLayout value.

import NoLayout from 'tsx-express'
import MyView from "../views/MyView"

app.get('/', (req, res, next) => {
      const properties : any = {}
      res.renderReact(MyView, properties, {layout:NoLayout} );
})

Overriding layouts

Additional setReactLayouts method on the response object allows you to set the layout and child layout.

This is handy for use elsewhere in your application when you might want to override layouts based on some other criteria. For example, for areas of an application, if a user is logged in, etc.

app.get('*', (req, res, next) => {
      if (res.locals.isAuthenticated) {
            res.setReactLayouts( NewChildLayout, MainLayout);
      }
      next();
})

Layouts

Your layouts will need a locals and children property. You can omit locals if you do not need them in your layout, but views will not render without a children element. You can extend the ILayoutProps interface or create your own. You do not need to set locals/children values as they are passed by the view engine.

export default function MyLayout({ children, locals }: ILayoutProps): ReactElement {
      return (
      <>
            {children}
      </>
      )
}

Note that locals are also typed and need to be defined or you'll get a "Property 'message does not exist" error. You can extend locals as follows :

declare global {
      namespace Express {
          interface Locals {
              yourProperty: string;
          }
      }
  }

Views

Similarly you may want to pass locals to your views. Again you can use the IViewProps interface, extend this, or create your own.

export default function MyView({ locals }: IViewProps): ReactElement {
      return (
      <>

      </>
      )
}

HTMX Tip

When using HTMX it can be useful to render a route with and without layouts so it can be loaded directly or from an HTMX request.

You can achieve this by detecting various HTMX headers such as hx-request and removing your layouts as required.

if (req.headers["hx-request"]) options = {layout:'noLayout', childLayout:'noLayout'}
res.renderReact(MyView,undefined,options)

If this is a pattern you use more generally you could also catch this similar to :

app.get('*', (req, res, next) => {
      if (req.headers["hx-request"]) {
            res.setReactLayouts( 'noLayout', 'noLayout');
      }
      next();
})