{"id":13612866,"url":"https://github.com/forgojs/forgo","last_synced_at":"2025-04-13T15:32:07.113Z","repository":{"id":39014733,"uuid":"329544874","full_name":"forgojs/forgo","owner":"forgojs","description":"An ultra-light UI runtime","archived":false,"fork":false,"pushed_at":"2024-09-12T17:30:37.000Z","size":708,"stargazers_count":322,"open_issues_count":9,"forks_count":9,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-10-12T20:42:16.690Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/forgojs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2021-01-14T07:54:08.000Z","updated_at":"2024-10-10T07:28:01.000Z","dependencies_parsed_at":"2024-06-18T18:18:51.896Z","dependency_job_id":"8578ca78-3cd7-4df4-8683-078bcc1912aa","html_url":"https://github.com/forgojs/forgo","commit_stats":{"total_commits":350,"total_committers":8,"mean_commits":43.75,"dds":"0.12857142857142856","last_synced_commit":"fbe7d28ad7b6d28d862e6f16e62cbf8df05649b9"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forgojs%2Fforgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forgojs%2Fforgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forgojs%2Fforgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/forgojs%2Fforgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/forgojs","download_url":"https://codeload.github.com/forgojs/forgo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223592308,"owners_count":17170473,"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":[],"created_at":"2024-08-01T20:00:35.661Z","updated_at":"2024-11-07T21:30:23.374Z","avatar_url":"https://github.com/forgojs.png","language":"TypeScript","readme":"# forgo\n\nForgo is a 4KB library that makes it super easy to create modern web apps using JSX (like React).\n\nUnlike React, there are very few framework specific patterns and lingo to learn. Everything you already know about DOM APIs and JavaScript will easily carry over.\n\n- Use HTML DOM APIs for accessing elements\n- There are no synthetic events\n- Use closures and ordinary variables for maintaining component state\n- There's no vDOM or DOM diffing\n- Renders are manually triggered\n- Declarative DOM updates\n\n## We'll be tiny. Always.\n\nAll of Forgo's code is in one single TypeScript file. It is a goal of the project to remain within that single file.\n\n## Installation\n\n```\nnpm install forgo\n```\n\n## Starting a Forgo project\n\nThe easiest way to get started is with the 'create-forgo-app' utility. This relies on git, so you should have git installed on your machine.\n\n```sh\nnpx create-forgo-app my-project\n```\n\nIt supports TypeScript too:\n\n```sh\nnpx create-forgo-app my-project --template typescript\n```\n\nAnd then to run it:\n\n```sh\n# Switch to the project directory\ncd my-project\n\n# Run!\nnpm start\n\n# To make a production build\nnpm run build\n```\n\n## A Forgo Component\n\nForgo components are functions that return a Component instance, which has a\n`render()` method that returns JSX. Components hold their state using ordinary\nvariables held in the closure scope of the function (called a Component Constructor).\n\nForgo likes to keep things simple and explicit. It avoids automatic behavior,\nprefers basic functions and variables instead of implicit constructs, and tries\nnot to come between you and the DOM.\n\nHere's the smallest Forgo component you can make:\n\n```jsx\nimport * as forgo from \"forgo\";\n\nconst HelloWorld = () =\u003e {\n  return new forgo.Component({\n    render() {\n      return \u003cp\u003eHello, world!\u003c/p\u003e;\n    }\n  });\n};\n```\n\nWhen a component is created (either by being mounted onto the DOM when the page\nloads, or because it was rendered by a component higher up in your app), Forgo\ncalls the Component Constructor to generate the component instance. This is\nwhere you can put closure variables to hold the component's state.\n\nThen Forgo calls the component's `render()` method to generate the HTML that\nForgo will show on the page.\n\nAfter the component's first render, the Constructor won't be called again, but\nthe `render()` method will be called each time the component (or one of its\nancestors) rerenders.\n\nForgo will pass any props (i.e., HTML attributes from your JSX) to both the\nConstructor and the `render()` method.\n\nHere's a bigger example - the component below increments a counter when a button is\npressed, and counts how many seconds the component has been alive.\n\n```jsx\nimport * as forgo from \"forgo\";\n\nconst ClickCounter = (initialProps) =\u003e {\n  let seconds = 0; // Just a regular variable, no hooks!\n  let clickCounter = 0;\n\n  const component = new forgo.Component({\n    // Every component has a render() method, which declares what HTML Forgo\n    // needs to generate.\n    render(props) {\n      const { firstName } = props;\n      // You can declare any DOM event handlers you need inside the render()\n      // method.\n      const onclick = (_event: Event) =\u003e {\n        // Forgo doesn't know or care how you manage your state. This frees you\n        // to use any library or code pattern that suits your situation, not\n        // only tools designed to integrate with the framework.\n        clickCounter += 1;\n        // When you're ready to rerender, just call component.update(). Manual\n        // updates mean the framework only does what you tell it to, putting you\n        // in control of efficiency and business logic.\n        //\n        // An optional package, forgo-state, can automate this for simple scenarios.\n        component.update();\n      };\n\n      // Forgo uses JSX, like React or Solid, to generate HTML declaratively.\n      // JSX is a special syntax for JavaScript, which means you can treat it\n      // like ordinary code (assign it to variables, type check it, etc.).\n      return (\n        \u003cdiv\u003e\n          \u003cp\u003eHello, {firstName}!\u003c/p\u003e\n          \u003cbutton type=\"button\" onclick={onclick}\u003e\n            The button has been clicked {clickCounter} times in {seconds} seconds\n          \u003c/button\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n\n  // You can add callbacks to react to lifecycle events,\n  // like mounting and unmounting\n  component.mount(() =\u003e {\n    const timeout = setTimeout(() =\u003e {\n      seconds++;\n      component.update();\n    }, 1000);\n    component.unmount(() =\u003e clearTimeout(timeout));\n  });\n\n  return component;\n};\n```\n\nHere's how the API looks when using TypeScript (which is optional):\n\n```tsx\nimport * as forgo from \"forgo\";\n\n// The constructor generic type accepts the shape of your component's props\nconst HelloWorld = () =\u003e {\n  return new forgo.Component({\n    render({ name }) {\n      return \u003cp\u003eHello, {name}!\u003c/p\u003e;\n    }\n  });\n};\n```\n\nIf you assign the component to a variable (such as when adding lifecycle event\nhandlers), you'll need to annotate the generic types on both the constructor and\nthe component. \n\nGeneric props can also be used:\n\n```tsx\nimport * as forgo from \"forgo\";\n\n// Props have to be assigned to the initial props for TSX to recognize the generic\ntype ListProps\u003cT extends string | number\u003e = {\n  data: T[];\n};\n\nconst List = \u003cT extends string | number\u003e(initial: ListProps\u003cT\u003e) =\u003e\n  new forgo.Component\u003cListProps\u003cT\u003e\u003e({\n    render(props) {\n      return (\n        \u003cul\u003e\n          {props.data.map((item) =\u003e (\n            \u003cli\u003e{item}\u003c/li\u003e\n          ))}\n        \u003c/ul\u003e\n      );\n    },\n  });\n\nconst App = () =\u003e\n  new forgo.Component({\n    render(props) {\n      return \u003cList data={[1, \"2\", 3]} /\u003e;\n    },\n  });\n```\n\n_If you're handy with TypeScript, [we'd love a PR to infer the types!](https://github.com/forgojs/forgo/issues/68)_\n\n```tsx\nimport * as forgo from \"forgo\";\n\ninterface HelloWorldProps {\n  name: string;\n}\nconst HelloWorld = () =\u003e {\n  const component = new forgo.Component\u003cHelloWorldProps\u003e({\n    render({ name }) {\n      return \u003cp\u003eHello, {name}!\u003c/p\u003e;\n    }\n  });\n\n  component.mount(() =\u003e console.log(\"Mounted!\"));\n\n  return component;\n};\n```\n\n## Launching your components when the page loads\n\nUse the mount() function once your document has loaded.\n\n```js\nimport { mount } from \"forgo\";\n\n// Wait for the page DOM to be ready for changes\nfunction ready(fn) {\n  if (document.readyState !== \"loading\") {\n    fn();\n  } else {\n    document.addEventListener(\"DOMContentLoaded\", fn);\n  }\n}\n\nready(() =\u003e {\n  // Attach your app's root component to a specific DOM element\n  mount(\u003cApp /\u003e, document.getElementById(\"root\"));\n});\n```\n\nInstead of retrieving the DOM element yoursely, you could pass a CSS selector\nand Forgo will find the element for you:\n\n```js\nready(() =\u003e {\n  mount(\u003cApp /\u003e, \"#root\");\n});\n```\n\n## Child Components and Passing Props\n\nProps and children work just like in React and similar frameworks:\n\n```jsx\n// Component Constructors will receive the props passed the *first* time the\n// component renders. But beware! This value won't be updated on later renders.\n// Props passod to the Constructor are useful for one-time setup, but to read\n// the latest props you'll need to use the value passed to render().\nconst Parent = (_initialProps) =\u003e {\n  return new forgo.Component({\n    // The props passed here will always be up-to-date.\n    //\n    // All lifecycle methods (render, mount, etc.) receive a reference to the\n    // component. This makes it easy to create reusable logic that works for\n    // many different components.\n    render(_props, _component) {\n      return (\n        \u003cdiv\u003e\n          \u003cGreeter firstName=\"Jeswin\" /\u003e\n          \u003cGreeter firstName=\"Kai\" /\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n};\n\nconst Greeter = (_initialProps) =\u003e {\n  return new forgo.Component({\n    render(props, _component) {\n      return \u003cdiv\u003eHello {props.firstName}\u003c/div\u003e;\n    }\n  });\n};\n```\n\nYou can pass any kind of value as a prop - not just strings! You just have to\nuse curly braces instead of quotes:\n\n```jsx\nconst MyComponent = () =\u003e {\n  return new forgo.Component({\n    render(_props) {\n      return \u003cNumberComponent myNumber={2} /\u003e;\n    }\n  });\n};\n```\n\nYou can have one component wrap JSX provided by another. To do this, just render `props.children`.\n\n```jsx\nconst Parent = () =\u003e {\n  return new forgo.Component({\n    render(_props) {\n      return\n        \u003cChild\u003e\n          \u003cp\u003eHello, world!\u003c/p\u003e\n        \u003c/Child\u003e\n      )\n    }\n  });\n}\n\nconst Child = () =\u003e {\n  return new forgo.Component({\n    render(props) {\n      return (\n        \u003cdiv\u003e\n          \u003cp\u003eHere's what the parent told us to render:\u003c/p\u003e\n          {props.children}\n        \u003c/div\u003e\n      )\n    }\n  });\n}\n```\n\n## Reading Form Input Elements\n\nForgo encourages you to use the vanilla DOM API when you need to read form field\nvalues, by directly accessing the DOM elements in the form.\n\nTo access the actual DOM elements corresponding to your markup (and the values\ncontained within them), you need to use the `ref` attribute in the JSX markup of\nthe element you want to reference. An element referenced by the `ref` attribute\nwill have its 'value' property set to the actual DOM element when it gets\ncreated.\n\nHere's an example:\n\n```jsx\nconst MyComponent = (_initialProps) =\u003e {\n  // This starts as an empty object. After the element is created, this object\n  // will have a `value` field holding the element.\n  const myInputRef = {};\n\n  return new forgo.Component({\n    render(_props, _component) {\n      const onClick = () =\u003e {\n        const inputElement = myInputRef.value;\n        alert(inputElement.value); // Read the text input.\n      };\n\n      return (\n        \u003cdiv\u003e\n          \u003cinput type=\"text\" ref={myInputRef} /\u003e\n          \u003cbutton type=\"button\" onclick={onClick}\u003eClick me!\u003c/button\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n};\n```\n\nIf you want, you can bypass Forgo entirely when reading form field values. If\nyou set the `id` field on the form field, then you could use the vanilla DOM API\nto access that element directly:\n\n```jsx\nconst onClick = () =\u003e {\n  const inputElement = document.getElementById(\"my-input\");\n  alert(inputElement.value);\n};\n```\n\nLastly, DOM events like key presses and clicks pass the affected element to the\nevent handler as `event.target`:\n\n```jsx\nconst Component = (_initialProps) =\u003e {\n  return new forgo.Component({\n    render(_props, _component) {\n      const onInput = (event) =\u003e {\n        alert(event.target.value);\n      };\n\n      return (\n        \u003cdiv\u003e\n          \u003cinput type=\"text\" oninput={onInput} /\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n};\n```\n\n## Rendering Lists and using Keys\n\nForgo will render any arrays it sees in the JSX. To create a list of elements,\njust use the array's `myArray.map()` method to generate JSX for each item in the array.\n\nEach item in the array may be given a `key` attribute. Keys help Forgo identify\nwhich items in a list have changed, are added, or are removed. While Forgo works\nwell without keys, it is a good idea to add them since it lets Forgo be more\nefficient by only mounting or unmounting components that actually need it.\n\nYou can use any data type for a key strings, numbers or even objects. The key\nvalues only need to be unique. Forgo compares keys using `===` (reference\nequality), so be careful when using mutable objects as keys.\n\nWhen looping over an array, don't use the array index as a key - keys should be\nsomething tied to the specific value being rendered (like a permanent ID field).\nThe same array index might be associated with different values if you reorder\nthe array, and so using the array index as a key will cause unexpected behavior.\n\n```jsx\nconst Parent = () =\u003e {\n  return new forgo.Component({\n    render(_props, _component) {\n      const people = [\n        { firstName: \"jeswin\", id: 123 },\n        { firstName: \"kai\", id: 456 },\n      ];\n      return (\n        \u003cdiv\u003e\n          {people.map((item) =\u003e (\n            \u003cChild key={item.id} firstName={item.firstName} /\u003e\n          ))}\n        \u003c/div\u003e\n      );\n    }\n  });\n};\n\nconst Child = (initialProps) =\u003e {\n  return new forgo.Component({\n    render(props) {\n      return \u003cdiv\u003eHello {props.firstName}\u003c/div\u003e;\n    },\n  });\n};\n```\n\n## Fetching data asynchronously\n\nYour component might need to load data asynchronously (such as making a network\nrequest). Here's how to do that:\n\n```jsx\nexport const InboxComponent = (_initialProps) =\u003e {\n  // This will be empty at first, and will get filled in sometime after the\n  // component first mounts.\n  let messages = undefined;\n\n  const component = new forgo.Component({\n    render(_props, _component) {\n      // Messages are empty. Let's fetch them.\n      if (!messages) {\n        return \u003cp\u003eLoading data...\u003c/p\u003e;\n      }\n\n      // After messages are fetched, the component will rerender and now we can\n      // show the data.\n      return (\n        \u003cdiv\u003e\n          \u003cheader\u003eYour Inbox\u003c/header\u003e\n          \u003cul\u003e\n            {messages.map((message) =\u003e (\n              \u003cli\u003e{message}\u003c/li\u003e\n            ))}\n          \u003c/ul\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n\n  component.mount(async () =\u003e {\n    messages = await fetchMessagesFromServer();\n    component.update();\n  });\n\n  return component;\n};\n```\n\n## The Mount Event\n\nThe mount event is fired just once per component, when the component has just\nbeen created. This is useful for set-up logic like starting a timer, fetching\ndata, or opening a WebSocket.\n\nYou can register multiple mount callbacks, which is useful if you want to have\nreusable logic that you apply to a number of components.\n\n```jsx\nconst Greeter = (_initialProps) =\u003e {\n  const component = new forgo.Component({\n    render(_props, _component) {\n      return \u003cdiv id=\"hello\"\u003eHello {props.firstName}\u003c/div\u003e;\n    }\n  });\n\n  component.mount((_props, _component) =\u003e {\n    console.log(\"The component has been mounted.\");\n  });\n\n  return component;\n};\n```\n\n## The Unmount Event\n\nA component is unmounted when your app no longer renders it (such as when a\nparent component chooses to display something different, or when an item is\nremoved from a list you're rendering).\n\nWhen a component is unmounted, you might want to do tear-down, like canceling a\ntimer or closing a WebSocket. To do this, you can register unmount callbacks\non your component, which will be called when the component is unmounted.\n\nThe callbacks are passed the current props and the component instance, just like\nthe `render()` method.\n\n```jsx\nconst Greeter = (_initialProps) =\u003e {\n  const component = new forgo.Component({\n    render(props, _component) {\n      return \u003cdiv\u003eHello {props.firstName}\u003c/div\u003e;\n    }\n  });\n\n  component.unmount((props, _component) =\u003e {\n    console.log(\"The component has been unloaded.\");\n  });\n\n  return component;\n};\n```\n\n## Skipping renders\n\nSometimes you have a reason why a component shouldn't be rendered right now. For\nexample, if you're using immutable data structures, you may want to only\nrerender if the data structure has changed.\n\nForgo components accept `shouldUpdate` callbacks, which return true/false to\nsignal whether the component should / should not be rerendered. If any\n`shouldUpdate` callbacks return true, the component will be rerendered. If they\nall return false (or if none are registered), the component's `render()` method\nwon't be called, skipping all DOM operations for the component and its\ndecendants.\n\nThe callbacks receive the new props for the proposed render, and the old props\nused in the last render.\n\nUsing `shouldUpdate` is completely optional, and typically isn't necessary.\n\n```jsx\nconst Greeter = (_initialProps) =\u003e {\n  const component = new forgo.Component({\n    render(props, component) {\n      return \u003cdiv\u003eHello {props.firstName}\u003c/div\u003e;\n    }\n  });\n\n  component.shouldUpdate((newProps, oldProps) =\u003e {\n    return newProps.firstName !== oldProps.firstName;\n  });\n\n  return component;\n}\n```\n\n## Error handling\n\nForgo lets components define an `error()` method, which is run any time the\ncomponent (or any of its decendants) throws an exception while running the\ncomponent's `render()` method. The error method can return JSX that is rendered\nin place of the render output, to display an error message to the user.\n\nIf no ancestors have an `error()` method registered, the render will abort and\nForgo will print an error to the console.\n\n```jsx\n// Here's a component which throws an error.\nconst BadComponent = () =\u003e {\n  return new forgo.Component({\n    render() {\n      throw new Error(\"Some error occurred :(\");\n    }\n  });\n}\n\n// The first ancestor with an error() method defined will catch the error\nconst Parent = (initialProps) =\u003e {\n  return new forgo.Component({\n    render() {\n      return (\n        \u003cdiv\u003e\n          \u003cBadComponent /\u003e\n        \u003c/div\u003e\n      );\n    },\n    error(props, error, _component) {\n      return (\n        \u003cp\u003e\n          Error in {props.name}: {error.message}\n        \u003c/p\u003e\n      );\n    }\n  });\n}\n```\n\n## The AfterRender Event\n\nIf you're an application developer you'll rarely need to use this - it's\nprovided for building libraries that wrap Forgo.\n\nThe `afterRender` event runs after `render()` has been called and the rendered\nelements have been created in the DOM. The callback is passed the previous DOM\nelement the component was attached to, if it changed in the latest render.\n\n```jsx\nconst Greeter = (_initialProps) =\u003e {\n  const component = new forgo.Component({\n    render(props, component) {\n      return \u003cdiv id=\"hello\"\u003eHello {props.firstName}\u003c/div\u003e;\n    }\n  });\n\n  component.afterRender((_props, previousNode, _component) =\u003e {\n    console.log(\n      `This component is mounted on ${component.__internal.element.node.id}, and was previously mounted on ${previousNode.id}`\n    );\n  });\n\n  return component;\n};\n```\n\n## Passing new props when rerendering\n\nThe most straight forward way to do rerender is by invoking it with `component.update()`, as follows:\n\n```jsx\nconst TodoList = (initialProps) =\u003e {\n  let todos = [];\n\n  return new forgo.Component({\n    render(props, component) {\n      const addTodos = (text) =\u003e {\n        todos.push(text);\n        component.update();\n      };\n      return (\n        \u003cbutton type=\"button\" onclick={addTodos}\u003e\n          Add a Todo\n        \u003c/button\u003e\n      );\n    }\n  });\n}\n```\n\n`component.update()` may optionally receive new props to use in the render.\nOmitting the props parameter will rerender leave the props unchanged.\n\n```js\nconst newProps = { name: \"Kai\" };\ncomponent.update(newProps);\n```\n\n## Rendering without mounting\n\nForgo also exports a render method that returns the rendered DOM node that could then be manually mounted.\n\n```jsx\nimport { render } from \"forgo\";\n\nconst { node } = render(\u003cComponent /\u003e);\n\nwindow.addEventListener(\"load\", () =\u003e {\n  document.getElementById(\"root\").firstElementChild.replaceWith(node);\n});\n```\n\n## Routing\n\nForgo offers an optional package (`forgo-router`) for handling client-side\nnavigation. Forgo Router is just around 1KB gzipped. Read more at\nhttps://github.com/forgojs/forgo-router\n\nHere's an example:\n\n```jsx\nimport { Router, Link, matchExactUrl, matchUrl } from \"forgo-router\";\n\nconst App = () =\u003e {\n  return new forgo.Component({\n    render() {\n      return (\n        \u003cRouter\u003e\n          \u003cLink href=\"/\"\u003eGo to Home Page\u003c/Link\u003e\n          {matchExactUrl(\"/\", () =\u003e \u003cHome /\u003e) ||\n            matchUrl(\"/customers\", () =\u003e \u003cCustomers /\u003e) ||\n            matchUrl(\"/about\", () =\u003e \u003cAboutPage /\u003e)}\n        \u003c/Router\u003e\n      );\n    }\n  });\n}\n```\n\n## Application State Management\n\nForgo offers an optional package (`forgo-state`) with an easy-to-use application\nstate management solution for Forgo. This solves a similar problem to Redux or\nMobX. It's than 1KB gzipped. Read more at https://github.com/forgojs/forgo-state\n\nHere's an example:\n\n```jsx\nimport { bindToStates, defineState } from \"forgo-state\";\n\n// Define one (or more) application state containers.\nconst mailboxState = defineState({\n  username: \"Bender B. Rodriguez\",\n  messages: [],\n  drafts: [],\n  spam: [],\n  unread: 0\n});\n\n// A Forgo component that should react to state changes\nconst MailboxView = () =\u003e {\n  const component = new forgo.Component({\n    render() {\n      if (mailboxState.messages.length \u003e 0) {\n        return (\n          \u003cdiv\u003e\n            {mailboxState.messages.map((m) =\u003e \u003cp\u003e{m}\u003c/p\u003e)}\n          \u003c/div\u003e\n        );\n      }\n\n      return (\n        \u003cdiv\u003e\n          \u003cp\u003eThere are no messages for {mailboxState.username}.\u003c/p\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n\n  component.mount(() =\u003e updateInbox());\n\n  // MailboxView must change whenever mailboxState changes.\n  //\n  // Under the hood, this registers component.mount() and component.unmount()\n  // even handlers\n  bindToStates([mailboxState], component);\n  return component;\n}\n\nasync function updateInbox() {\n  const data = await fetchInboxData();\n  // The next line causes a rerender of the MailboxView component\n  mailboxState.messages = data;\n}\n```\n\n## Lazy Loading\n\nIf you want to lazy load a component, you can use the community-provided\n`forgo-lazy` package. This is useful for code splitting, where you want the\ninitial page load to be quick (loading the smallest JS possible), and then load\nin more components only when the user needs them. Read more at\nhttps://github.com/jacob-ebey/forgo-lazy\n\nIt's works like this:\n\n```jsx\nimport lazy, { Suspense } from \"forgo-lazy\";\n\nconst LazyComponent = lazy(() =\u003e import(\"./lazy-component\"));\n\nconst App = () =\u003e {\n  return new forgo.Component({\n    render() {\n      return (\n        \u003cSuspense fallback={() =\u003e \"Loading...\"}\u003e\n          \u003cLazyComponent title=\"It's that easy :D\" /\u003e\n        \u003c/Suspense\u003e\n      );\n    }\n  });\n}\n```\n\n## Integrating Forgo into an existing app\n\nForgo can be integrated into an existing web app written with other frameworks\n(React, Vue, etc.), or with lower-level libraries like jQuery.\n\nTo help with that, the `forgo-powertoys` package (less than 1KB in size) exposes\na `rerenderElement()` function which can receive a CSS selector and rerender the\nForgo component associated with that element. This works from outside the Forgo\napp, so you can drive Forgo components using your framework/library of choice.\nRead more at https://github.com/forgojs/forgo-powertoys\n\nHere's an example:\n\n```jsx\nimport { rerenderElement } from \"forgo-powertoys\";\n\n// A forgo component.\nconst LiveScores = () =\u003e {\n  return new forgo.Component({\n    render(props) {\n      return \u003cp id=\"live-scores\"\u003eTop score is {props.topscore}\u003c/p\u003e;\n    }\n  });\n}\n\n// Mount it on a DOM node usual\nwindow.addEventListener(\"load\", () =\u003e {\n  mount(\u003cSimpleTimer /\u003e, document.getElementById(\"root\"));\n});\n\n// Now you can rerender the component from anywhere, anytime! Pass in the ID of\n// the root element the component returns, as well as new props.\nrerenderElement(\"#live-scores\", { topscore: 244 });\n```\n\n## Server-side Rendering (SSR)\n\nFrom Node.js you can render components to an HTML string with the `forgo-ssr`\npackage. This allows you to prerender components on the server, from server-side\nframeworks like Koa, Express etc. Read more at\nhttps://github.com/forgojs/forgo-ssr\n\nHere's an example:\n\n```jsx\nimport render from \"forgo-ssr\";\n\n// A forgo component.\nconst MyComponent = () =\u003e {\n  return new forgo.Component({\n    render() {\n      return \u003cdiv\u003eHello world\u003c/div\u003e;\n    }\n  });\n}\n\n// Get the html (string) and serve it via koa, express etc.\nconst html = render(\u003cMyComponent /\u003e);\n```\n\n## Manually adding elements to the DOM\n\nForgo allows you to use the built-in browser DOM API to insert elements into the\nDOM tree rendered by a Forgo component. Forgo will ignore these elements. This\nis useful for working with charting libraries, such as D3.\n\nIf you add unmanaged nodes as siblings to nodes which Forgo manages, Forgo\npushes the unmanaged nodes towards the bottom of the sibling list when managed\nnodes are added and removed. If you don't add/remove managed nodes, the\nunmanaged nodes will stay in their original positions.\n\n### ApexCharts example\n\n[Code Sandbox](https://codesandbox.io/s/forgo-apexcharts-demo-ulkqfe?file=/src/index.tsx) for this example\n\n```jsx\nconst App = () =\u003e {\n  const chartElement = {};\n\n  const component = new forgo.Component({\n    render(_props, component) {\n      const now = new Date();\n      return (\n        \u003cdiv\u003e\n          \u003cp\u003e\n            This component continually rerenders. Forgo manages the timestamp,\n            but delegates control of the chart to ApexCharts.\n          \u003c/p\u003e\n          \u003cdiv ref={chartElement}\u003e\u003c/div\u003e\n          \u003cp\u003e\n            The current time is:{\" \"}\n            \u003ctime datetime={now.toISOString()}\u003e{now.toLocaleString()}\u003c/time\u003e\n          \u003c/p\u003e\n        \u003c/div\u003e\n      );\n    }\n  });\n\n  component.mount(() =\u003e {\n    const chartOptions = {\n      chart: {\n        type: \"line\",\n      },\n      series: [\n        {\n          name: \"sales\",\n          data: [30, 40, 35, 50, 49, 60, 70, 91, 125],\n        },\n      ],\n      xaxis: {\n        categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999],\n      },\n    };\n\n    const chart = new ApexCharts(chartElement.value, chartOptions);\n\n    chart.render();\n\n    const interval = setInterval(() =\u003e component.update(), 1_000);\n    component.unmount(() =\u003e clearInterval(interval));\n  });\n\n  return component;\n};\n```\n\n## Try it out on CodeSandbox\n\nYou can try the [Todo List app with Forgo](https://codesandbox.io/s/forgo-todos-javascript-1oi9b) on CodeSandbox.\n\nOr if you prefer TypeScript, try [Forgo TodoList in TypeScript](https://codesandbox.io/s/forgo-todos-typescript-9v0iy).\n\nThere is also an example for using [Forgo with forgo-router](https://codesandbox.io/s/forgo-router-typescript-px4sg).\n\n## Building\n\nMost users should use create-forgo-app to create the project skeleton - in which\ncase all of this is already set up for you. This is the easiest way to get\nstarted.\n\nIf you want to stand up a project manually, we'll cover webpack-specific\nconfiguration here. Other bundlers would need similar configuration.\n\n### esbuild-loader with JavaScript/JSX\n\nAdd these lines to webpack.config.js:\n\n```js\nmodule.exports = {\n  // remaining config omitted for brevity.\n  module: {\n    rules: [\n      {\n        test: /\\.(js|jsx)$/,\n        exclude: /node_modules/,\n        loader: \"esbuild-loader\",\n        options: {\n          loader: \"jsx\",\n          target: \"es2015\",\n          jsxFactory: \"forgo.createElement\",\n          jsxFragment: \"forgo.Fragment\",\n        },\n      },\n    ],\n  },\n};\n```\n\n### esbuild-loader with TypeScript/TSX\n\nAdd these lines to webpack.config.js:\n\n```js\nmodule.exports = {\n  // remaining config omitted for brevity.\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        exclude: /node_modules/,\n        loader: \"esbuild-loader\",\n        options: {\n          loader: \"tsx\",\n          target: \"es2015\",\n          jsxFactory: \"forgo.createElement\",\n          jsxFragment: \"forgo.Fragment\",\n        },\n      },\n    ],\n  },\n};\n```\n\nWhile using TypeScript, also add the following lines to your tsconfig.json. This lets you do `tsc --noEmit` for type checking, which esbuild-loader doesn't do.\n\nAdd these lines to tsconfig.json:\n\n```json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react\",\n    \"jsxFactory\": \"forgo.createElement\",\n    \"jsxFragmentFactory\": \"forgo.Fragment\"\n  }\n}\n```\n\n### babel-loader with JSX\n\nThis is slower than esbuild-loader, so use only as needed.\n\nAdd these lines to webpack.config.js:\n\n```js\nmodule.exports = {\n  // remaining config omitted for brevity.\n  module: {\n    rules: [\n      {\n        test: /\\.(js|jsx)$/,\n        exclude: /node_modules/,\n        use: [\"babel-loader\"],\n      },\n    ],\n  },\n};\n```\n\nAdd these lines to babel.config.json:\n\n```json\n{\n  \"presets\": [\"@babel/preset-env\", \"@babel/preset-react\"],\n  \"plugins\": [\n    [\"@babel/plugin-transform-react-jsx\", { \"pragma\": \"forgo.createElement\" }]\n  ]\n}\n```\n\n### TSX with ts-loader\n\nAdd these lines to webpack.config.js:\n\n```js\nmodule.exports = {\n  // remaining config omitted for brevity.\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        use: \"ts-loader\",\n        exclude: /node_modules/,\n      },\n    ],\n  },\n};\n```\n\nAdd these lines to tsconfig.json:\n\n```json\n{\n  \"compilerOptions\": {\n    \"jsx\": \"react\",\n    \"jsxFactory\": \"forgo.createElement\",\n    \"jsxFragmentFactory\": \"forgo.Fragment\"\n  }\n}\n```\n\n## Core Team\n\n- [github/jeswin](https://github.com/jeswin)\n- [github/spiffytech](https://github.com/spiffytech)\n\n## Getting Help\n\nIf you find issues, please file a bug on\n[Github](https://github.com/forgojs/forgo/issues). You can also reach out to us\nvia Twitter (@forgojs).\n\n## Deprecation of legacy component syntax is 3.2.0\nIn version 3.2.0, Forgo introduced a new syntax for components. This change\nmakes Forgo easier to extend with reusable libraries, and makes it\nstraightforward to colocate logic that spans mounts \u0026 unmounts.\n\nThe legacy component syntax will be removed in v4.0. Until then, Forgo will\nprint a warning to the console whenever it sees a legacy component. You can\nsuppress these warnings by setting `window.FORGO_NO_LEGACY_WARN = true;`.\n\n### Migrating\nForgo components are now instances of the `Component` class, rather than\nfreestanding object values. The `new Component` constructor accepts an object\nholding a `render()` an optional `error()` method. All other methods have been\nconverted to lifecycle methods on the component instance. You may register\nmultiple handlers for each lifecycle event, and you may register new handlers\nfrom inside a handler (e.g., a mount handler that registers its own unmount\nlogic).\n\n`args` has been replaced by a reference to the component instance, in all\nlifecycle event handlers. This simplifies writing reusable component logic.\n\nThe `error()` method now receives the error object as a function parameter,\nrather than as a property on `args`.\n\nThe `afterRender` lifecycle event now receives the `previousNode` as a function\nparameter, instead of a property on `args`.\n\nBefore:\n```jsx\nconst MyComponent = () =\u003e {\n  return {\n    render() {},\n    error() {},\n    mount() {},\n    unmount() {},\n    shouldUpdate() {},\n    afterRender() {},\n  };\n}\n```\n\nAfter:\n```jsx\nconst MyComponent = () =\u003e {\n  const component = new Component({\n    render() {},\n    error() {}\n  });\n\n  component.mount(() =\u003e {});\n  component.unmount(() =\u003e {});\n  component.shouldUpdate(() =\u003e {});\n  component.afterRender(() =\u003e {});\n\n  return component;\n}\n```\n\n## Breaking changes in 2.0\n\nForgo 2.0 drops support for the new JSX transform introduced via \"jsx-runtime\".\nThis never worked with esbuild loader, and more importantly doesn't play well\nwith ES modules. If you were using this previously, switch to the configurations\ndiscussed above.","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforgojs%2Fforgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fforgojs%2Fforgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fforgojs%2Fforgo/lists"}