{"id":19568295,"url":"https://github.com/ernestmarcinko/htmx-serverless","last_synced_at":"2025-10-09T23:03:52.732Z","repository":{"id":200380363,"uuid":"705656046","full_name":"ernestmarcinko/htmx-serverless","owner":"ernestmarcinko","description":"HTMX Serverless XHR requests. A frontend tool to define custom responses to XHR requests based on the request Path.","archived":false,"fork":false,"pushed_at":"2023-10-18T11:43:31.000Z","size":49,"stargazers_count":17,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-07T04:46:52.957Z","etag":null,"topics":["dom","dom-manipulation","extension","htmx","htmx-app","jquery","library"],"latest_commit_sha":null,"homepage":"https://ernestmarcinko.com/htmx-serverless/","language":"TypeScript","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/ernestmarcinko.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-16T12:49:13.000Z","updated_at":"2024-11-06T18:31:51.000Z","dependencies_parsed_at":"2023-10-16T18:54:15.379Z","dependency_job_id":"eabaf10b-0b96-437a-b177-8b6be2039abd","html_url":"https://github.com/ernestmarcinko/htmx-serverless","commit_stats":null,"previous_names":["ernestmarcinko/htmx-serverless"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ernestmarcinko/htmx-serverless","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ernestmarcinko%2Fhtmx-serverless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ernestmarcinko%2Fhtmx-serverless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ernestmarcinko%2Fhtmx-serverless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ernestmarcinko%2Fhtmx-serverless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ernestmarcinko","download_url":"https://codeload.github.com/ernestmarcinko/htmx-serverless/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ernestmarcinko%2Fhtmx-serverless/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002215,"owners_count":26083340,"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","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["dom","dom-manipulation","extension","htmx","htmx-app","jquery","library"],"created_at":"2024-11-11T06:03:18.187Z","updated_at":"2025-10-09T23:03:52.688Z","avatar_url":"https://github.com/ernestmarcinko.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HTMX Serverless Client States ![npm](https://img.shields.io/npm/v/htmx-serverless) ![npm](https://img.shields.io/npm/dy/htmx-serverless) ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)\n\nTo use HTMX you require a back-end server to handle the XHR requests and responses. In some cases it is nice to have only a client side interaction to handle client states, **without network requests**.\n\nThis extension uses the HTMX built-in Events to intercept some XHR requests before they fire and define response texts on the client side. No need for mock or \"fake\" server scripts. It is **HTMX without a server** (sort of).\n\n## Usage\n\n### In HTML head\n\n```html\n\u003c!-- htmx  --\u003e\n\u003cscript src=\"https://unpkg.com/htmx.org\"\u003e\u003c/script\u003e\n\u003c!-- serverless extension --\u003e\n\u003cscript src=\"https://unpkg.com/htmx-serverless\"\u003e\u003c/script\u003e\n```\n\nThen use the `window.htmxServerless` global to set custom handlers and responses.\n\n```javascript\n// Requests to \"/handler1\" are replaced with \"\u003cdiv\u003eCustom HTML\u003c/div\u003e\"\nhtmxServerless.handlers.set('/handler1', '\u003cdiv\u003eCustom HTML\u003c/div\u003e');\n\n// Requests to \"/handler2\" are managed via a function\nhtmxServerless.handlers.set('/handler2', function(text, params, xhr){\n    console.log(this, text, params, xhr);\n    return \"\u003cp\u003eOkay!\u003c/p\u003e\";\n});\n\n// Directly within the hx-{request} attribute, return value of myFunc is the replacement\n\u003cbutton hx-get=\"js:myFunc\" hx-swap=\"outerHTML\" hx-ext=\"serverless\"\u003e\n    Click to replace via myFunc!\n\u003c/button\u003e\n```\n\n### In custom bundles\n\n```javascript\nimport htmx from \"htmx.org\";\nimport htmxServerless from \"htmx-serverless\";\n\n// Initialize on your local htmx\nhtmxServerless.init(htmx);\n```\n\n## Examples\n\n### Handler as a string\n\nAssume we have a button with the `serverless` **hx-ext** sattribute, which triggers a request to the path \"/clicked\":\n\n```html\n\u003cbutton hx-get=\"/clicked\" hx-swap=\"outerHTML\" hx-ext=\"serverless\"\u003e\n    Click to replace!\n\u003c/button\u003e\n```\n\nTo define a serverless client side response to \"/clicked\" in the handlers Map():\n\n```javascript\nhtmxServerless.handlers.set('/clicked', \n    `\u003cbutton hx-get=\"/clicked\" hx-swap=\"outerHTML\" hx-ext=\"serverless\"\u003e\n        Hey, you clicked me!\n    \u003c/button\u003e`\n);\n```\n\nThe button is then replaced with the HTML defined without triggering a request to the server. It's that simple.\n\n[Try this example here.](https://jsfiddle.net/ernestmarcinko/h0rj5pez/1/)\n\n### Handler as a Function set excplicitly\n\nTha handler function is a great tool for more complex conditional logic, like it would happen on the server side.\nLet's make a simple click based number increment handler:\n\n```html\n\u003cbutton hx-get=\"/count\" \n        hx-target=\"next .counter\" \n        hx-trigger=\"load, click\" \n        hx-vals='js:{myVal: i++}' \n        hx-ext=\"serverless\"\u003eClick to Increment\u003c/button\u003e\n\u003cspan class=\"counter\"\u003e\u003c/span\u003e\n   \n```\n\nThe handler only needs to print the text as \"i\" is incremented by hx-vals automatically:\n\n```javascript\nlet i = 0;\nhtmxServerless.handlers.set('/count', function(text, params, xhr){\n    let status = params?.myVal \u003c 10 ? \"smaller or equals to\" : \"bigger than\";\n    return `Value of \"myVal\" is: ${params?.myVal}, it is ${status} 10.`;\n});\n```\n\n[Try this example here.](https://jsfiddle.net/ernestmarcinko/fm4tu9q8/2/)\n\nOutput:\n\n![Alt text](img/auto-increment.png)\n\n### Handler as a Function via js:myFunc\n\nYou can set the handler function implicitly in the hx-{get,post etc..} attribute via the js:myFunc syntax:\n\n```html\n\u003cbutton hx-get=\"js:counter\" \n        hx-target=\"next .counter\" \n        hx-trigger=\"load, click\" \n        hx-vals='js:{myVal: i++}' \n        hx-ext=\"serverless\"\u003eClick to Increment\u003c/button\u003e\n\u003cspan class=\"counter\"\u003e\u003c/span\u003e\n   \n```\n\nThe handler function accepts the same arguments as before:\n\n```javascript\nlet i = 0;\nfunction counter(text, params, xhr){\n    let status = params?.myVal \u003c 10 ? \"smaller or equals to\" : \"bigger than\";\n    return `Value of \"myVal\" is: ${params?.myVal}, it is ${status} 10.`;\n}\n```\n\n[Try this example here.](https://jsfiddle.net/ernestmarcinko/x3kdownf/3/)\n\n## Handler function\n\nThe handler function accepts 3 parameters (4 including \"this\") and returns a string:\n * **this** =\u003e The target element\n * **ext** =\u003e The replacement text (empty)\n * **params** =\u003e The GET/POST or xhr-vals arguments\n * **xhr** =\u003e The current request\n\n```javascript\n/**\n * The handler function\n * \n * @param this:Element The target element\n * @param text:string The replacement text (empty)\n * @param params:Object The GET/POST or xhr-vals arguments\n * @param xhr:XMLHttpRequest The current request\n * \n * @returns string\n */ \nfunction handler(text, params, xhr){\n    console.log(this, text, params, xhr);\n    return 'Hi!';\n}\n```\n\n## How does it work?\nIt is really simple:\n- The XHR request will not be sent, the ```.send()``` method is overridden for the intercepted request\n- The XHR ```loadstart```, ```load``` and ```loadend``` events are dispatched instead, as if the request was finished \"successfully\"\n- Only requests added to the ```htmxServerless.handlers``` Map are intercepted\n- Requests are intercepted based on the request path, request arguments does not matter\n\n## What else?\n\nNothing actually. This is only a baseline solution, but it works. There are no fancy features, as htmx is oath to be a small but effective library. With some creativity, you could make this more convenient, I leave it up to you :)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fernestmarcinko%2Fhtmx-serverless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fernestmarcinko%2Fhtmx-serverless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fernestmarcinko%2Fhtmx-serverless/lists"}