{"id":19309353,"url":"https://github.com/levyks/esbeltojs","last_synced_at":"2026-04-09T16:01:57.789Z","repository":{"id":57227287,"uuid":"396492595","full_name":"Levyks/esbeltoJS","owner":"Levyks","description":"A simple view engine for Express with a Svelte-like syntax","archived":false,"fork":false,"pushed_at":"2021-09-03T14:32:16.000Z","size":47,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-06T15:42:49.651Z","etag":null,"topics":["express","nodejs","svelte","view-engine"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Levyks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-08-15T20:55:08.000Z","updated_at":"2021-09-03T14:32:19.000Z","dependencies_parsed_at":"2022-08-25T12:21:55.611Z","dependency_job_id":null,"html_url":"https://github.com/Levyks/esbeltoJS","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Levyks%2FesbeltoJS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Levyks%2FesbeltoJS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Levyks%2FesbeltoJS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Levyks%2FesbeltoJS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Levyks","download_url":"https://codeload.github.com/Levyks/esbeltoJS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240410378,"owners_count":19796882,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["express","nodejs","svelte","view-engine"],"created_at":"2024-11-10T00:18:43.546Z","updated_at":"2026-04-09T16:01:57.715Z","avatar_url":"https://github.com/Levyks.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# esbeltoJS\n\nA simple view engine for Express with a Svelte-like syntax\n\n## How to use\n```\nnpm install esbelto\n```\n```js\nconst express = require('express');\nconst esbelto = require('esbelto');\n\nconst app = express();\n\n/*You can use 'svelte', 'esb', or any other extension\n *just make sure to configure your editor to treat it as a .svelte file\n */\napp.engine('svelte', esbelto.express);\napp.set('view engine', 'svelte');\n```\n\n### Passing values\n```js\napp.get('/', (req, res) =\u003e {\n  res.render('index', { user: { name: \"John\" } });\n});\n```\nindex.svelte:\n```svelte\n\u003cscript id=\"esbelto\"\u003e\n  let { user } = getVariables();\n\u003c/script\u003e\n\n\u003chead\u003e\n  \u003ctitle\u003eEsbelto\u003c/title\u003e\n\u003c/head\u003e\n\n\u003cbody\u003e\n  \u003ch1\u003eWelcome {user.name}!\u003c/h1\u003e\n\u003c/body\u003e\n```\n![Welcome John](https://i.imgur.com/vVogPzE.png)\n\n---\n\n### {#if}, {:else} and {:else if}\n```js\napp.get('/if', (req, res) =\u003e {\n  res.render('if', { \n    user: {\n      isOwner: false,\n      isAdmin: true\n    }\n  });\n});\n```\nif.svelte: \n```svelte\n{#if user.isOwner}\n  \u003cbutton\u003eOwner dashboard\u003c/button\u003e\n{:else if user.isAdmin}\n  \u003cbutton\u003eAdmin dashboard\u003c/button\u003e\n{:else}\n  \u003cbutton\u003eUser dashboard\u003c/button\u003e\n{/if}\n```\n![If example](https://i.imgur.com/TImXMt4.png)\n\n---\n\n### {#each}\n```js\napp.get('/each', (req, res) =\u003e {\n  res.render('each', {\n    books: [\n      \"The Hitchhiker's Guide to the Galaxy\",\n      \"The Restaurant at the End of the Universe\",\n      \"Life, the Universe and Everything\",\n      \"So Long, and Thanks for All the Fish\",\n      \"Mostly Harmless\"\n    ]\n  });\n});\n```\neach.svelte:\n```svelte\n{#each books as book}\n  \u003ch2\u003e{book}\u003c/h2\u003e\n{/each}\n\u003cbr\u003e\n{#each books as book, idx}\n  \u003ch2\u003e{idx+1}° -\u003e {book}\u003c/h2\u003e\n{/each}\n```\n![Each example](https://i.imgur.com/R5K65Nq.png)\n\n---\n\n### Escaping {@html}\nJust as in Svelte, Esbelto also escapes HTML by default, if you do not want to escape it, just add a `@html` after the `{`\n```js\napp.get('/escaping', function (req, res) {\n  res.render('escaping', {\n    title: '\u003cb\u003eThis is the title\u003c/b\u003e'\n  });\n});\n```\nescaping.svelte:\n```svelte\n\u003cp\u003e{title}\u003c/p\u003e\n\u003cp\u003e{@html title}\u003c/p\u003e\n```\n![Escaping example](https://i.imgur.com/LXDch0V.png)\n\n---\n### include and includeScript\n\nMost editors for .svelte files (like Svelte's extension for VSCode) will warn you if you have more than one `\u003cscript\u003e` tag in your code.\nThis isn't a problem for Esbelto, but, if you want to avoid that warning, you can use the includeScript method(You can use either an string with the script's src or an object with each desired property of the `\u003cscript\u003e` tag)\n\ninclude.svelte:\n```svelte\n\u003cscript id=\"esbelto\"\u003e\n  let include = getInclude();\n\u003c/script\u003e\n\n\u003chead\u003e\n  {include('./head.svelte', {title: \"Testing include\", scripts: ['js/main.js']})}\n\u003c/head\u003e\n\n\u003cbody\u003e\n  \u003cspan class='text-success ms-1'\u003eHello World!\u003c/span\u003e\n\u003c/body\u003e  \n```\nhead.svelte:\n```svelte\n\u003cscript id=\"esbelto\"\u003e\n  let includeScript = getIncludeScript();\n  let { title, scripts } = getVariables();\n\u003c/script\u003e\n\n\u003ctitle\u003e{title}\u003c/title\u003e\n\n\u003clink rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css\" integrity=\"sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We\" crossorigin=\"anonymous\"\u003e\n\n{includeScript({\n  src: \"https://code.jquery.com/jquery-3.6.0.min.js\",\n  integrity: \"sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=\",\n  crossorigin: \"anonymous\"\n})}\n\n{#if scripts}\n  {#each scripts as script}\n    {includeScript(script)}\n  {/each}\n{/if}\n```\n![Include example](https://user-images.githubusercontent.com/16294244/130052111-6a13be9d-cfe3-4156-a8ca-a10c76336164.png)\n\n---\n### config\nYou can tweak some settings by using the `esbelto.config()` method, although it's not required\n```js\nconst esbelto = require('./esbelto');\nconst express = require('express');\n\nconst app = express();\n\n//These are the defaults\nesbelto.config({\n  htmlStartTag: '\u003c!DOCTYPE html\u003e\\n\u003chtml\u003e\\n',\n  htmlEndTag: '\\n\u003c/html\u003e',\n  cacheCompileds: true,\n  cacheSettings: {\n    storeOnDisk: false,\n    recompileOnChange: true\n});\n\napp.engine('svelte', esbelto.express);\napp.set('view engine', 'svelte');\n```\n- `cacheCompiled` -\u003e makes every render after the 1st faster, as it does not have to recompile the template every request\n- `storeOnDisk` -\u003e setting this to true will make esbelto store the compiled template in a file in the same folder as the template, as opposed to in memory, in my tests, this option was actually slower than `cacheCompiled: false`, but it could be useful when working with some really big templates\n- `recompileOnChange` -\u003e checks the last modified date of the template, and recompiles it if it was changed, really useful during development, but I recommend setting this to false in production, as it makes the render a bit slower, this option is always true when using `storeOnDisk: true`, and, therefore, ignored\n\n#### Cache settings performance comparisons\nThis was the template used for these tests: \nuser-dashboard.svelte:\n```svelte\n\u003cscript id=\"esbelto\"\u003e\n  let include = getInclude();\n  let { user } = getVariables();\n\u003c/script\u003e\n\n\u003chead\u003e\n  {include('./partials/head.svelte', {title: \"User Dashboard\"})}\n\u003c/head\u003e\n\n\u003cbody\u003e\n  \u003ch1\u003e{user.name}\u003c/h1\u003e\n  \u003ch3\u003eAge: {user.age}\u003c/h3\u003e\n  \u003ch4\u003eChildren:\u003c/h4\u003e\n  \u003cul\u003e\n  {#each user.children as child}\n    \u003cli class=\"{child.gender == 'M' ? 'blue' : 'pink'}\"\u003e{child.name} - {child.age}\u003c/li\u003e\n  {/each}\n  \u003c/ul\u003e\n  {#if user.isAdmin || user.isOwner}\n    \u003cspan\u003eSome administrative data\u003c/span\u003e\n    \u003cbr\u003e\n    {#if user.isOwner}\n      \u003cbutton\u003eGo to owner panel\u003c/button\u003e\n    {:else if user.isAdmin}\n      \u003cbutton\u003eGo to admin panel\u003c/button\u003e\n    {/if}\n  {/if}\n\u003c/body\u003e\n```\n/partials/head.svelte:\n```svelte\n\u003cscript id=\"esbelto\"\u003e\n  let { title } = getVariables();\n\u003c/script\u003e\n\n\u003ctitle\u003e{title}\u003c/title\u003e\n\u003clink rel=\"stylesheet\" href=\"/css/style.css\"\u003e\n```\nData used:\n```json\n{ \n  \"user\": {\n    \"name\": \"John Doe\",\n    \"age\": 42,\n    \"gender\": \"M\",\n    \"isAdmin\": true,\n    \"children\": [\n      {\n        \"name\": \"John Doe Jr\",\n        \"gender\": \"M\",\n        \"age\": 11\n      },\n      {\n        \"name\": \"Jane Doe\",\n        \"gender\": \"F\",\n        \"age\": 19\n      }\n    ]\n  }\n}\n```\n##### Results:\n```\nSettings:  { cacheCompileds: false }\n\nRendering 1000 times:\n\nFirst render: 2.3007ms\nAverage of all renders but the first: 0.2948488488488488ms\nTotal time elapsed 296.8547ms\n\n--------------------------\nSettings:  { cacheCompileds: true } //Default\n\nRendering 1000 times:\n\nFirst render: 3.2604ms\nAverage of all renders but the first: 0.17305155155155155ms\nTotal time elapsed 176.1389ms\n\n--------------------------\nSettings:  { cacheCompileds: true, cacheSettings: { recompileOnChange: false } }\n\nRendering 1000 times:\n\nFirst render: 1.8947ms\nAverage of all renders but the first: 0.008698698698698699ms\nTotal time elapsed 10.5847ms\n\n--------------------------\nSettings:  { cacheCompileds: true, cacheSettings: { storeOnDisk: true } }\n\nRendering 1000 times:\n\nFirst render: 1.7583ms\nAverage of all renders but the first: 0.6899459459459459ms\nTotal time elapsed 691.0143ms\n```\nBeware that the difference between caching and not caching will be greatly bigger with more complex templates\n\n---\n### Benchmark with other view engines\n\nThis is not by any means official, just a quick test I made forking [baryshev's benchmark](https://github.com/baryshev/template-benchmark) and adding esbelto to it, full results available at [Levyks/template-benchmark](https://github.com/Levyks/template-benchmark)\n```\nRendering 100000 templates:\n\nesbeltoJS\n Escaped   : 1334ms\n Unescaped : 39ms\n Total     : 1373ms\n\nEJS\n Escaped   : 3090ms\n Unescaped : 1443ms\n Total     : 4533ms\n\nEJS without `with`\n Escaped   : 1288ms\n Unescaped : 50ms\n Total     : 1338ms\n\nPug\n Escaped   : 4063ms\n Unescaped : 47ms\n Total     : 4110ms\n\nPug without `with`\n Escaped   : 3594ms\n Unescaped : 27ms\n Total     : 3621ms\n ...\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevyks%2Fesbeltojs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flevyks%2Fesbeltojs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevyks%2Fesbeltojs/lists"}