{"id":43270595,"url":"https://github.com/treeder/rend","last_synced_at":"2026-02-01T15:41:34.152Z","repository":{"id":163561256,"uuid":"625397514","full_name":"treeder/rend","owner":"treeder","description":"the pure JavaScript server-side renderer","archived":false,"fork":false,"pushed_at":"2025-03-31T05:53:01.000Z","size":170,"stargazers_count":24,"open_issues_count":16,"forks_count":2,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-31T06:28:46.546Z","etag":null,"topics":["framework","javascript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/treeder.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-09T01:31:53.000Z","updated_at":"2025-03-31T05:53:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"51599ba8-6f45-4b1f-8ca8-8e41a94acc6b","html_url":"https://github.com/treeder/rend","commit_stats":null,"previous_names":[],"tags_count":93,"template":false,"template_full_name":null,"purl":"pkg:github/treeder/rend","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treeder%2Frend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treeder%2Frend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treeder%2Frend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treeder%2Frend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/treeder","download_url":"https://codeload.github.com/treeder/rend/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/treeder%2Frend/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28981456,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T15:35:50.179Z","status":"ssl_error","status_checked_at":"2026-02-01T15:35:38.075Z","response_time":56,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","javascript"],"created_at":"2026-02-01T15:41:33.515Z","updated_at":"2026-02-01T15:41:34.146Z","avatar_url":"https://github.com/treeder.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rend - the pure JavaScript server-side renderer \n\n\u003cpre\u003e\n                   _________\n_________________________  /\n__  ___/  _ \\_  __ \\  __  / \n_  /   /  __/  / / / /_/ /  \n/_/    \\___//_/ /_/\\__,_/   \n\u003c/pre\u003e\n\nA light-weight, no dependency, JavaScript renderer inspired by [Lit](https://lit.dev). While Lit is awesome for client side \ncomponents, Rend provides a similar experience on the server-side. It's all standard, modern JavaScript using interpolation\nwhich enables you to do any kind of JavaScript tricks you want without using some proprietary syntax. Your server side code \nand syntax is nearly the same as the client side.\n\nUse Rend for server side and [Lit](https://lit.dev) web components for the client side. A perfect match. \n\n[Live Demo](https://rend-giosppuxqq-uc.a.run.app/)\n\nI love constructive feedback, so please post any questions, feedback or ideas [here](https://github.com/treeder/rend/discussions).\n\n[Subscribe here](https://thingster.app/orgs/treeder/spaces/rend) to get latest updates, tips and tricks.  \n\n## Upgrading to 1.X\n\n[See here](https://github.com/treeder/rend/discussions/36)\n\n## Benefits\n\n* No build - ie: zero build time!\n* No lock-in\n* No proprietary syntax like with other template engines\n* Server side rendering\n* Client side rendering - use web components! They are built into the browser and are awesome.\n\nThe philosophy behind this is based on islands architecture or components architecture where you server side render all the static\nthings for super fast first contentful paint then you hydrate the dynamic areas with JavaScript to make them usable. \n\n## Quickstart\n\nThis repo is Codespaces ready, just click the button below, then at the terminal run `make run`\n\n[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/treeder/rend?quickstart=1)\n\n## Getting Started\n\nJust install rend:\n\n```sh\nnpm install treeder/rend\n```\n\n## Usage\n\n### NEW - Islands / Component Islands / Slots\n\nWhatever you want to call it, the [basic idea](https://thingster.app/things/qsXjgXN2TD6CsL5gpmVRd) is to server side render the layout of your app and static content, then drop in client side components for interactivity. So users get a lightning fast initial page load while still having all the interactivity we expect in an app.\n\nFirst we make a `layout.js` file and we add content slots to it. This example has a navigation rail/drawer area and the main content area:\n\n```js\nexport async function layout(d) {\n    return `\n    ${header(d)}\n\n    \u003cdiv class=\"container\"\u003e\n        \u003cdiv class=\"flex\" style=\"gap: 12px;\"\u003e\n            \u003cdiv\u003e${await slot('rail', d)}\u003c/div\u003e\n            \u003cdiv\u003e${await slot('main', d)}\u003c/div\u003e\n        \u003c/div\u003e\n    \u003c/div\u003e\n\n    ${footer(d)}\n    `\n}\n```\n\nThen you use that layout like this:\n\n```js\n// Initialize rend with your main layout:\nlet rend = new Rend({\n    layout: layout,\n    data: { apiURL },\n})\n\n// In your request handler:\nlet d = {\n    // slotted content:\n    rail: './views/drawer.js', // direct import, must have export a render function\n    main: './views/index.js', // or use class components\n    // the rest of your data\n    name: \"John Wick\",\n    car: \"Mustang Boss 429\",\n    greeting: msg('Hello, how are you?', {\n        id: 'greeting', // This is the localization ID to lookup in the es.js file\n        locale: 'es', // Snag the user's locale from a cookie, or 'Accept-Language' or something instead of hardcoding here.\n    }),\n}\nreturn rend.html(d)\n```\n\nThen make a JavaScript file for each view, we'll start with `views/index.js`:\n\n```js\nimport { html } from 'rend'\n\nexport function render(d) {\n  return html`\n    \u003ch2\u003eHello ${d.name}!\u003c/h2\u003e\n  `\n}\n```\n\nTo change the response status, simply add a status field:\n\n```js\nreturn rend.html({\n   main: './views/404.js', \n   status: 404,\n})\n```\n\nYou can find a full example of this [here](https://github.com/treeder/rend/tree/2886f788da4a2b5ab51048b0eb51b98f0316f5d9/examples/bun-hono).\n\n## Server Side Rendering - SSR\n\nThe examples above are all server side code. If you run it and view it, you'll see it render insanely fast.\n\nBecause this is all JavaScript based you can do everything as you normally would in JavaScript. Here's some examples:\n\n#### Loops\n\nHere's how to loop and output:\n\n```js\nexport function render(d) {\n  return html`\n    \u003ch2\u003eHello ${d.name}!\u003c/h2\u003e\n    These are your tasks for today:\n    \u003cul\u003e\n      ${d.tasks.map(t) =\u003e `\u003cli\u003e${t.name}\u003c/li\u003e`}\n    \u003c/ul\u003e\n  `\n}\n```\n\n#### Conditionals\n\n```js\nexport function render(d) {\n  return html`\n    \u003ch2\u003eHello ${d.name ? d.name : 'World'}!\u003c/h2\u003e\n  `\n}\n```\n\n## Server Side Components\n\nServer-side components are reusable classes that render on the server. These are generally static, but can contain client-side components\nto make them interactive. \n\nThe basic structure is to make a class with a render method. If you want to get the data object into it, create a `constructor` function like\nthe example below. \n\nHere is an example of a server-side component:\n\n```js\nimport { html } from 'rend'\n\nexport class MyReusableComponent {\n    constructor(d) {\n        this.d = d\n    }\n\n    render(d) {\n        return html`\n        \u003cdiv style=\"border: 1px solid blue; border-radius: 8px; padding: 10px;\"\u003e\n            \u003cdiv class=\"mb-3\"\u003e\n                I am a reusable server-side component. \u003cspan class=\"blue\"\u003eHello ${this.d.name}.\u003c/span\u003e\n            \u003c/div\u003e\n            \u003cdiv class=\"\"\u003e\n                \u003c!-- This is a client-side web component: --\u003e\n                \u003cscript type=\"module\"\u003e\n                    import '/components/hello-world.js'\n                \u003c/script\u003e\n                \u003chello-world name=\"${this.d.name}\" car=\"${this.d.car}\"\u003e\u003c/hello-world\u003e\n            \u003c/div\u003e\n        \u003c/div\u003e\n        `\n    }\n}\n```\n\nThen you can use it inside your pages `render` function:\n\n```js\nimport { html } from 'rend'\nimport { MyReusableComponent } from './components/my-reusable-component.js'\n\nexport function render(d) {\n    return html`\n        \u003ch3\u003eServer-side components\u003c/h3\u003e\n        \u003cdiv class=\"mt-3\"\u003e\n            ${new MyReusableComponent(d)}\n        \u003c/div\u003e\n    `\n}\n```\n\n## Using with various platforms\n\n\n### Bun with Hono\n\nThis is the recommended path as Bun and Hono are more modern and use standard APIs whereas Node has it's own non-standard APIs. \n\nExample:\n\n```js\napp.get('/', async (c) =\u003e {\n    let d = {\n        name: \"John Wick\",\n        car: \"Mustang Boss 429\",\n    }\n    return rend.html('./views/index.js', d)\n})\n```\n\nThat's it! Full example available at [examples/bun-hono](examples/bun-hono).\n\n### Cloudflare Pages Functions\n\nVery similar to the rest, here is an example `functions/index.js` that will serve at `/` in your Cloudflare Pages app:\n\n```js\nimport { Rend, html } from 'rend'\nimport { header, footer } from './_layout.js'\n\nlet rend = new Rend({ header, footer, prod: true })\n\nexport function onRequest(context) {\n    return rend.html(index, {name: 'Honey'})\n}\n\nfunction index(d) {\n    return html`\n      ${d.name}, I'm home!\n    `\n}\n```\n\n### Node with Fastify \n\nThis is a fastify example, but you can do the same with Express or whatever you like to use. \n\n```js\nimport { Rend } from 'rend'\nimport { header, footer } from './views/layout.js'\n\n// Initialize Rend with header and footer render functions:\nlet rend = new Rend({ header, footer }) // other options found in code such as prod: true for extra performance\n\nfastify.get('/', async (request, reply) =\u003e {\n    // The following will write the response using the template at index.js and a data object you can use in your template:\n    return rend.send(reply, './views/index.js', {name: 'John Wick'})\n})\n```\n\nTo see the full example of this, see [examples/node-fastify](examples/node-fastify).\n\nStart it up with `node server.js` and surf to https://localhost:3000. That's it!\n\n\n## Client Side - Web Components\n\nWeb components are standard technology that is now supported in every major browser. This eliminates the need\nto use things like React that were created before web components were a thing. Because it's part of the browser \nyou'll get better performance, fast builds and no lock-in!\n\nI recommend using [Lit](https://lit.dev) to help you use web components, it's a lightweight wrapper around the web components API.\n\n### Quick example\n\nCopy the following into a file called `hello-world.js`, make sure it's in a publicly accessible folder. \n\n```js\nimport {html, css, LitElement} from 'lit'\n\nexport class HelloWorld extends LitElement {\n  static styles = css`p { color: blue }`\n\n  static properties = {\n    name: {type: String},\n  }\n\n  constructor() {\n    super()\n    this.name = 'Somebody'\n  }\n\n  render() {\n    return html`\u003cp\u003eHello, ${this.name}!\u003c/p\u003e`\n  }\n}\ncustomElements.define('hello-world', HelloWorld)\n```\n\nThen in your `render()` function for your view, just need to import and use it:\n\n```js\n\u003cscript type=\"module\"\u003e\n    import '/components/hello-world.js'\n\u003c/script\u003e\n\u003chello-world name=\"${d.name}\"\u003e\u003c/hello-world\u003e\n```\n\nIt's that simple! See the [example](examples/) apps for working examples.\n\n## Localization\n\nI recommend using the [Loco](https://github.com/treeder/loco) library as it's very simple and has some really\nnice convenience features. The [example](/example) app uses it to show how easy it is.\n\nThe very nice thing is that it is Lit compatible you can use @lit/localize and loco with the same language\nfiles. \n\nThis is being used in the [example app](/example) and [demo](https://rend-giosppuxqq-uc.a.run.app/). \n\n## Other helpful functions\n\n### stringify - a special version for web components\n\nA little enhancement to JSON.stringify so that it works with HTML attributes. Use this if you want to pass objects into a web or Lit component.\n\nNOTE: you do NOT need to use this if you are using the `html` tag function in your render method, it will automatically do this for you.\n\n```js\nimport { Rend } from 'rend'\n\u003cmy-component something=\"${Rend.stringify(obj)}\"\u003e\u003c/my-component\u003e\n```\n\n### head - generates a nice and optimized head section\n\nA nice little function to generate a nice and optimized `\u003chead\u003e` section. It will help with lazy loading fonts, set all your opengraph and twitter \nmeta tags, etc. \n\nUse this in your layout.js `header(d)` function:\n\n```js\nimport {head} from 'rend/head.js'\n\nexport function header(d){\n  // this can be your entire header function:\n  return html`\n  ${head({\n    title: 'My web app!',\n  })}\n  `\n}\n```\n\nSee [examples](examples/) for more of the fields you can pass, like description, fonts, styles, etc.\n\n## Good Practice Guidelines\n\nHere's some things we find useful that make building your apps more consistent. \n\n### Errors\n\nUse `cause` to wrap errors: \n\n```js\ntry {\n  something()\n} catch (err) {\n  throw new Error(\"New error message\", { cause: err })\n}\n```\n\nThen you can check the cause with `err.cause`.\n\n### API / HTTP Errors\n\nUse the following:\n\n```js\nexport class APIError extends Error {\n    constructor(message, options) {\n      super(message, options)\n      this.status = options.status\n    }\n\n    toString() {\n        return `${this.status} ${this.message}`;\n    }\n}\n```\n\n## Development\n\n### Codespaces (recommended)\n\nTo get everything setup out of the box, simply open this repo in a codespace and run: `make run`.\n\n### Local\n\nClone this repo.\n\nSetup:\n\n```sh\nmake install\n```\n\nRun:\n\n```sh\nmake run\n```\n\nbump\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftreeder%2Frend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftreeder%2Frend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftreeder%2Frend/lists"}