{"id":22311538,"url":"https://github.com/vilicvane/backpage","last_synced_at":"2025-07-29T08:32:14.754Z","repository":{"id":216886259,"uuid":"742612686","full_name":"vilicvane/backpage","owner":"vilicvane","description":"Naive static HTML streaming based on React for Node.js CLI applications.","archived":false,"fork":false,"pushed_at":"2024-05-13T11:02:34.000Z","size":270,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-25T03:07:38.400Z","etag":null,"topics":["cli","gui","html","nodejs","react"],"latest_commit_sha":null,"homepage":"https://backpage.cloud","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vilicvane.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":"2024-01-12T21:42:32.000Z","updated_at":"2025-02-28T20:53:38.000Z","dependencies_parsed_at":"2024-03-22T01:45:06.844Z","dependency_job_id":null,"html_url":"https://github.com/vilicvane/backpage","commit_stats":null,"previous_names":["vilicvane/backpage"],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/vilicvane/backpage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fbackpage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fbackpage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fbackpage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fbackpage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vilicvane","download_url":"https://codeload.github.com/vilicvane/backpage/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vilicvane%2Fbackpage/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267653230,"owners_count":24122161,"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-07-29T02:00:12.549Z","response_time":2574,"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":["cli","gui","html","nodejs","react"],"created_at":"2024-12-03T21:20:15.268Z","updated_at":"2025-07-29T08:32:14.467Z","avatar_url":"https://github.com/vilicvane.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![NPM version](https://img.shields.io/npm/v/backpage?color=%23cb3837\u0026style=flat-square)](https://www.npmjs.com/package/backpage)\n[![Repository package.json version](https://img.shields.io/github/package-json/v/vilicvane/backpage?color=%230969da\u0026label=repo\u0026style=flat-square)](./package.json)\n[![MIT License](https://img.shields.io/badge/license-MIT-999999?style=flat-square)](./LICENSE)\n[![Discord](https://img.shields.io/badge/chat-discord-5662f6?style=flat-square)](https://discord.gg/wEVn2qcf8h)\n\n# BackPage \u003c!-- omit in toc --\u003e\n\nNaive static HTML streaming based on React for Node.js CLI applications.\n\n## How does it work? \u003c!-- omit in toc --\u003e\n\nBackPage renders your React application to HTML and streams updates (**static** HTML snapshots) to your browser.\n\nIt is designed for really simple GUI as a complementary to text logs, so **advanced user interaction is neither supported nor its goal.**\n\n## Features \u003c!-- omit in toc --\u003e\n\n- Stream static HTML from React rendering.\n- Send notification to browser.\n- Simple user interaction with HTML form.\n- Simple webhook usages with `/action/[action-name]` and `/notify` endpoints.\n- Public URL via [backpage.cloud](https://backpage.cloud).\n\n## Table of Contents \u003c!-- omit in toc --\u003e\n\n- [Installation](#installation)\n- [Basic Usage](#basic-usage)\n- [Form-based Interaction](#form-based-interaction)\n- [Events](#events)\n  - [click](#click)\n  - [input](#input)\n- [Browser Notification](#browser-notification)\n- [Action](#action)\n- [BackPage Cloud](#backpage-cloud)\n- [Examples](#examples)\n- [Built-in Components](#built-in-components)\n  - [Form](#form)\n  - [ActionButton](#actionbutton)\n  - [Title](#title)\n  - [Style](#style)\n  - [Console](#console)\n\n## Installation\n\n```bash\nnpm install react backpage\n```\n\n## Basic Usage\n\n**main.tsx**\n\n```tsx\nimport {BackPage} from 'backpage';\nimport React from 'react';\n\nimport {App} from './app.js';\n\nconst page = new BackPage();\n\npage.render(\u003cApp /\u003e);\n\n// Print page information including URL.\npage.guide();\n```\n\n**app.tsx**\n\n```tsx\nimport React, {useState, useEffect} from 'react';\n\nexport const App = () =\u003e {\n  const [count, setCount] = useState(0);\n\n  useEffect(() =\u003e {\n    const timer = setInterval(\n      setCount(count =\u003e count + 1),\n      1000,\n    );\n\n    return () =\u003e clearInterval(timer);\n  }, []);\n\n  return \u003cdiv\u003eCount: {count}\u003c/div\u003e;\n};\n```\n\n## Form-based Interaction\n\nSee [Form](#form) and [ActionButton](#actionbutton) for simple usage.\n\n## Events\n\nBackPage can proxy explicitly specified events that bubble to `document` from the browser to your Node.js React application.\n\n```tsx\nconst page = new BackPage({\n  events: ['click'],\n});\n\npage.render(\n  \u003cdiv\n    onClick={() =\u003e {\n      console.info('This will work.');\n    }}\n  \u003e\n    Click me!\n  \u003c/div\u003e,\n);\n```\n\n\u003e Events are proxied asynchronously, and just for the purpose of triggering actions in your Node.js application.\n\n\u003e Not all events bubble, please checkout relevant documents for more information.\n\n### click\n\nProperties:\n\n- `altKey`\n- `ctrlKey`\n- `metaKey`\n- `shiftKey`\n\n### input\n\nEffects:\n\n- Sets `event.target.value` to the value of the input element.\n\n## Browser Notification\n\nA notification can be sent to the browser using either `page.notify()` or `/notify` endpoint.\n\nTo send notification to the browser using `page.notify()`:\n\n```ts\npage.notify('Hello BackPage!');\n\npage.notify({\n  title: 'Hello BackPage!',\n  body: 'This is a notification from BackPage.',\n});\n```\n\nYou can also setup a fallback for notifications not getting **clicked** within the timeout:\n\n```ts\nconst page = new BackPage({\n  notify: {\n    // timeout: 30_000,\n    fallback: notification =\u003e {\n      // Handle the notification manually.\n\n      // Optionally return a webhook URL or request options to initiate an HTTP\n      // request.\n      return 'https://some.webhook/';\n    },\n  },\n});\n\npage.notify({\n  title: 'Hello BackPage!',\n  body: 'Click me or your webhook will get fired!',\n});\n```\n\nTo send notification to the browser using `/notify` endpoint:\n\n```ts\nawait fetch('http://localhost:12368/notify', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    title: 'Hello BackPage!',\n    body: 'This is a notification from BackPage.',\n  }),\n});\n```\n\n\u003e A `timeout` field can also be specified (a number or `false`) to override the default value.\n\n## Action\n\nA simple webhook can be setup with `/action/[action-name]` endpoint.\n\n```ts\npage.registerAction('hello', data =\u003e {\n  console.info('Hello', data);\n});\n```\n\nTo trigger this action:\n\n```ts\nawait fetch('http://localhost:12368/action/hello', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json',\n  },\n  body: JSON.stringify({\n    name: 'BackPage',\n  }),\n});\n```\n\nThis uses the same mechanism as the `Form` action, so you may want to avoid using the same action name (if you explicitly specify one for `Form`).\n\n## BackPage Cloud\n\nBy specifying a UUID as token, you can get a public URL from [backpage.cloud](https://backpage.cloud):\n\n```ts\nimport {BackPage, getPersistentToken} from 'backpage';\n\nconst page = new BackPage({\n  // You can also use any random UUID for temporary page.\n  token: getPersistentToken(),\n  // Different pages can be setup using the same token with different names.\n  // name: 'project-name',\n});\n\npage.guide();\n```\n\nYou can also create a temporary front only page for web notifications using \u003chttps://backpage.cloud/new\u003e.\n\n\u003e **Note:** [backpage.cloud](https://backpage.cloud) may introduce value-added services for significant network traffic to cover the expense in the future.\n\n## Examples\n\nCheck out [src/examples](./src/examples).\n\n## Built-in Components\n\n### Form\n\nA `Form` is based on HTML `form` element and has similar usage, except that `action` is proxied backed by `POST` requests and accepts callback with the form data object as its parameter.\n\n```tsx\nconst action = data =\u003e console.info(data);\n\npage.render(\n  \u003cForm action={action}\u003e\n    \u003cinput name=\"name\" /\u003e\n    \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n  \u003c/Form\u003e,\n);\n```\n\n### ActionButton\n\nIn many cases, only the button is relevant for an action. `ActionButton` wraps a button within a `Form` for those cases:\n\n```tsx\nconst action = () =\u003e console.info('Launch!');\n\npage.render(\u003cActionButton action={action}\u003eLaunch\u003c/ActionButton\u003e);\n```\n\nYou can also put multiple `ActionButton`s in an explicit `Form` to share the form inputs:\n\n```tsx\nconst actionA = data =\u003e console.info('action-a', data);\nconst actionB = data =\u003e console.info('action-b', data);\n\npage.render(\n  \u003cForm\u003e\n    \u003cinput name=\"name\" /\u003e\n    \u003cActionButton action={actionA}\u003eAction A\u003c/ActionButton\u003e\n    \u003cActionButton action={actionB}\u003eAction B\u003c/ActionButton\u003e\n  \u003c/Form\u003e,\n);\n```\n\n### Title\n\nSets `document.title` of the page.\n\n```tsx\npage.render(\n  \u003c\u003e\n    \u003cTitle\u003eAwesome Page\u003c/Title\u003e\n    \u003cdiv\u003eHello BackPage!\u003c/div\u003e\n  \u003c/\u003e,\n);\n```\n\n\u003e You can also specify `title` in `BackPage` options if it not dynamic.\n\n### Style\n\nAdds a `style` element to the page with content loaded from `src` (local path).\n\n```tsx\nconst App = () =\u003e (\n  \u003c\u003e\n    \u003cStyle src={STYLE_PATH} /\u003e\n    \u003cdiv\u003eHello BackPage!\u003c/div\u003e\n  \u003c/\u003e\n);\n```\n\n\u003e You can directly use `\u003clink rel=\"stylesheet\" href=\"...\" /\u003e` for CSS links.\n\n### Console\n\nIntercepts console outputs using [patch-console](https://www.npmjs.com/package/patch-console).\n\n```tsx\nconst App = () =\u003e (\n  \u003c\u003e\n    \u003ch2\u003eLogs\u003c/h2\u003e\n    \u003cConsole /\u003e\n  \u003c/\u003e\n);\n```\n\n## License \u003c!-- omit in toc --\u003e\n\nMIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvilicvane%2Fbackpage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvilicvane%2Fbackpage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvilicvane%2Fbackpage/lists"}