express-tsx-views
Version:
Server-side JSX/TSX rendering for your express or NestJS application 🚀
273 lines (205 loc) • 8.59 kB
Markdown
<div align="center">
<p>Server-side JSX/TSX rendering for your express or NestJS application</p>
</div>
[](https://www.npmjs.com/package/express-tsx-views)
[![Test Coverage][coveralls-image]][coveralls-url]
[![Build Status][build-image]][build-url]
# Description <!-- omit in toc -->
With this template engine, TSX files can be rendered server-side by your Express application. Unlike other JSX express renderers, this one does not rely on JSX files being transpiled by `babel` at runtime. Instead, TSX files are processed once by the `tsc` compiler.
For this to work, the templates are imported dynamically during rendering. And for this **you have to provide a default export in your main TSX files**. (Embeddable TSX components don't have to use a default export).
# Highlights <!-- omit in toc -->
- Fast, since the JSX/TSX files do not have to be transpiled on-the-fly with every request
- Works with compiled files (`.js` / `node`) and uncompiled files (`.tsx` / `ts-node`, `ts-jest`, ...)
- Provides the definition of React contexts on middleware level
- Supports execution of GraphQL queries from JSX components
# Table of contents <!-- omit in toc -->
- [Usage](#usage)
- [Express](#express)
- [NestJS](#nestjs)
- [Render Middlewares](#render-middlewares)
- [Prettify](#prettify)
- [Provide React Context](#provide-react-context)
- [GraphQL](#graphql)
- [License](#license)
# Usage
```sh
$ npm install --save express-tsx-views
```
You have to set the `jsx` setting in your TypeScript configuration `tsconfig.json` to the value `react` and to enable `esModuleInterop`:
```json
{
"compilerOptions": {
"jsx": "react",
"esModuleInterop": true
}
}
```
This template engine can be used in express and NestJS applications. The function `setupReactViews()` is provided, with which the engine is made available to the application.
```ts
import { setupReactViews } from "express-tsx-views";
const options = {
viewsDirectory: path.resolve(__dirname, "../views"),
};
setupReactViews(app, options);
```
The following options may be passed:
| Option | Type | Description | Default |
| ---------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
| `viewsDirectory` | `string` | The directory where your views (`.tsx` files) are stored. Must be specified. | - |
| `doctype` | `string` | [Doctype](https://developer.mozilla.org/en-US/docs/Glossary/Doctype) to be used. | `<!DOCTYPE html>\n` |
| `transform` | `(html: string) => string` | With this optional function the rendered HTML document can be modified. For this purpose a function must be defined which gets the HTML `string` as argument. The function returns a modified version of the HTML string as `string`. | - |
| `middlewares` | `TsxRenderMiddleware[]` | A list of `TsxRenderMiddleware` objects that can be used to modify the render context. See [Render middlewares](#render-middlewares) | - |
## Express
Example express app (See also `example/app.ts` in this project):
```js
import express from "express";
import { resolve } from "path";
import { setupReactViews } from "express-tsx-views";
import { Props } from "./views/my-view";
export const app = express();
setupReactViews(app, {
viewsDirectory: resolve(__dirname, "views"),
prettify: true, // Prettify HTML output
});
app.get("/my-route", (req, res, next) => {
const data: Props = { title: "Test", lang: "de" };
res.render("my-view", data);
});
app.listen(8080);
```
`views/my-view.tsx`:
```tsx
import React, { Component } from "react";
import MyComponent from "./my-component";
import { MyLayout } from "./my-layout";
export interface Props {
title: string;
lang: string;
}
// Important -- use the `default` export
export default class MyView extends Component<Props> {
render() {
return <div>Hello from React! Title: {this.props.title}</div>;
}
}
```
## NestJS
See [nestjs-tsx-views](https://github.com/pmb0/nestjs-tsx-views).
express-tsx-views can also be used in [NestJS](https://nestjs.com/). For this purpose the template engine must be made available in your `main.ts`:
# Render Middlewares
## Prettify
Prettifies generated HTML markup using [prettier](https://github.com/prettier/prettier).
```ts
setupReactViews(app, {
middlewares: [new PrettifyRenderMiddleware()],
});
```
## Provide React Context
Provides a react context when rendering your react view.
```ts
// my-context.ts
import {createContext} from 'react'
export interface MyContextProps = {name: string}
export const MyContext = createContext<MyContextProps | undefined>(undefined)
```
Use `addReactContext()` to set the context in your route or in any other middleware:
```ts
// app.ts
// Route:
app.get("/", (request: Request, res: Response) => {
addReactContext(res, MyContext, { name: "philipp" });
res.render("my-view");
});
// Middleware:
app.use((req: Request, res: Response, next: NextFunction) => {
addReactContext(res, MyContext, {
name: "philipp",
});
next();
});
```
Now you can consume the context data in any component:
```tsx
// my-component.tsx
import { useContext } from "react";
import { MyContext } from "./my-context";
export function MyComponent() {
const { name } = useContext(MyContext);
return <span>Hallo, {name}!</span>;
}
```
## GraphQL
This module supports the execution of GraphQL queries from the TSX template. For this purpose `graphql`, `/client` and `cross-fetch` have to be installed separately:
```sh
$ npm install --save /client cross-fetch
```
Now you can create an `ApolloRenderMiddleware` object and configure it as a middleware within `express-tsx-views`:
```ts
import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
import { ApolloRenderMiddleware } from "express-tsx-views/dist/apollo";
// needed to create a apollo client HTTP link:
import { fetch } from "cross-fetch";
// Apollo client linking to an example GraphQL server
const apollo = new ApolloClient({
ssrMode: true,
link: createHttpLink({
uri: "https://swapi-graphql.netlify.app/.netlify/functions/index",
fetch,
}),
cache: new InMemoryCache(),
});
setupReactViews(app, {
viewsDirectory: resolve(__dirname, "views"),
middlewares: [new ApolloRenderMiddleware(apollo)],
});
```
Example view (see the example folder in this project):
```ts
export interface Film {
id: string;
title: string;
releaseDate: string;
}
export interface AllFilms {
allFilms: {
films: Film[];
};
}
const MY_QUERY = gql`
query AllFilms {
allFilms {
films {
id
title
releaseDate
}
}
}
`;
export interface Props {
title: string;
lang: string;
}
export default function MyView(props: Props): ReactElement {
const { data, error } = useQuery<AllFilms>(MY_QUERY);
if (error) {
throw error;
}
return (
<MyLayout lang={props.lang} title={props.title}>
<h2>Films:</h2>
{data?.allFilms.films.map((film) => (
<ul key={film.id}>
{film.title} ({new Date(film.releaseDate).getFullYear()})
</ul>
))}
</MyLayout>
);
}
```
# License
express-tsx-views is distributed under the MIT license. [See LICENSE](./LICENSE) for details.
[coveralls-image]: https://img.shields.io/coveralls/pmb0/express-tsx-views/master.svg
[coveralls-url]: https://coveralls.io/r/pmb0/express-tsx-views?branch=master
[build-image]: https://github.com/pmb0/express-tsx-views/workflows/Tests/badge.svg
[build-url]: https://github.com/pmb0/express-tsx-views/actions?query=workflow%3ATests