{"id":13457761,"url":"https://github.com/frejs/fre","last_synced_at":"2025-05-13T20:14:00.128Z","repository":{"id":37663280,"uuid":"142654069","full_name":"frejs/fre","owner":"frejs","description":":ghost: Tiny Concurrent UI library with Fiber.","archived":false,"fork":false,"pushed_at":"2025-04-23T11:40:29.000Z","size":7162,"stargazers_count":3738,"open_issues_count":13,"forks_count":347,"subscribers_count":39,"default_branch":"master","last_synced_at":"2025-05-13T01:41:48.099Z","etag":null,"topics":["fiber","fre","hook","hooks","jsx","keyed-reconcilation-algorithm","react-hooks","react-like","reconcilation","reconciliation-algorithm","vdom","vdom-library"],"latest_commit_sha":null,"homepage":"https://fre.deno.dev","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/frejs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null},"funding":{"open_collective":"fre","github":"yisar"}},"created_at":"2018-07-28T06:46:46.000Z","updated_at":"2025-05-12T02:58:00.000Z","dependencies_parsed_at":"2023-09-23T14:36:35.477Z","dependency_job_id":"c0434eec-7473-4da8-8bda-c00196ee36f0","html_url":"https://github.com/frejs/fre","commit_stats":{"total_commits":1742,"total_committers":75,"mean_commits":"23.226666666666667","dds":0.2652123995407577,"last_synced_commit":"6c3475ad5234050fa830ac9830c7bbd9c514a5ee"},"previous_names":["yisar/fre"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frejs%2Ffre","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frejs%2Ffre/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frejs%2Ffre/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/frejs%2Ffre/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/frejs","download_url":"https://codeload.github.com/frejs/fre/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253958101,"owners_count":21990548,"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":["fiber","fre","hook","hooks","jsx","keyed-reconcilation-algorithm","react-hooks","react-like","reconcilation","reconciliation-algorithm","vdom","vdom-library"],"created_at":"2024-07-31T09:00:35.872Z","updated_at":"2025-05-13T20:14:00.110Z","avatar_url":"https://github.com/frejs.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\r\n\r\n\u003cp\u003e\u003cimg src=\"https://user-images.githubusercontent.com/44045911/147237798-174728c9-7399-4b47-be39-78ef69198a0d.png\" alt=\"fre logo\" width=\"130\"\u003e\u003c/p\u003e\r\n\u003ch1\u003eFre\u003c/h1\u003e\r\n\u003cp\u003e👻 Tiny Concurrent UI library with Fiber.\u003c/p\u003e\r\n\r\n![GitHub License](https://img.shields.io/github/license/frejs/fre)\r\n[![Build Status](https://img.shields.io/github/actions/workflow/status/yisar/fre/main.yml)](https://github.com/yisar/fre/actions)\r\n[![Code Coverage](https://img.shields.io/codecov/c/github/frejs/fre.svg)](https://codecov.io/gh/yisar/fre)\r\n[![npm-v](https://img.shields.io/npm/v/fre.svg)](https://npmjs.com/package/fre)\r\n[![npm-d](https://img.shields.io/npm/dt/fre.svg)](https://npmjs.com/package/fre)\r\n[![brotli](http://img.badgesize.io/https://unpkg.com/fre/dist/fre.js?compression=brotli\u0026label=brotli)](https://bundlephobia.com/result?p=fre)\r\n\r\n\u003c/div\u003e\r\n\r\n\r\n- **Concurrent Mode** — This is an amazing idea, which implements the coroutine scheduler in JavaScript, it also called **Time slicing**.\r\n\r\n- **Keyed reconcilation algorithm** — Fre has a minimal longest-common-subsequence algorithm, It supported keyed, pre-process.\r\n\r\n- **Do more with less** — After tree shaking, project of hello world is only 1KB, but it has most features, virtual DOM, hooks API, Fragments, Fre.memo and so on.\r\n\r\n### Contributors\r\n\r\n\u003ca href=\"https://github.com/yisar/fre/graphs/contributors\"\u003e\r\n  \u003cimg src=\"https://contrib.rocks/image?repo=yisar/fre\" /\u003e\r\n\u003c/a\u003e\r\n\r\n### Usage\r\n\r\n```shell\r\nyarn add fre\r\n```\r\n\r\n```js\r\nimport { render, useState } from 'fre'\r\n\r\nfunction App() {\r\n  const [count, setCount] = useState(0)\r\n  return \u003c\u003e\r\n      \u003ch1\u003e{count}\u003c/h1\u003e\r\n      \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003e+\u003c/button\u003e\r\n    \u003c/\u003e\r\n}\r\n\r\nrender(\u003cApp/\u003e, document.body)\r\n```\r\n\r\n### Hooks API\r\n\r\n- [useState](https://github.com/yisar/fre#usestate)\r\n\r\n- [useEffect](https://github.com/yisar/fre#useeffect)\r\n\r\n- [useReducer](https://github.com/yisar/fre#usereducer)\r\n\r\n- [useLayout](https://github.com/yisar/fre#uselayout)\r\n\r\n- [useCallback](https://github.com/yisar/fre#usecallback)\r\n\r\n- [useMemo](https://github.com/yisar/fre#usememo)\r\n\r\n- [useRef](https://github.com/yisar/fre#useref)\r\n\r\n#### useState\r\n\r\n`useState` is a base API, It will receive initial state and return an Array\r\n\r\nYou can use it many times, new state is available when component is rerender\r\n\r\n```js\r\nfunction App() {\r\n  const [up, setUp] = useState(0)\r\n  const [down, setDown] = useState(0)\r\n  return (\r\n    \u003c\u003e\r\n      \u003ch1\u003e{up}\u003c/h1\u003e\r\n      \u003cbutton onClick={() =\u003e setUp(up + 1)}\u003e+\u003c/button\u003e\r\n      \u003ch1\u003e{down}\u003c/h1\u003e\r\n      \u003cbutton onClick={() =\u003e setDown(down - 1)}\u003e-\u003c/button\u003e\r\n    \u003c/\u003e\r\n  )\r\n}\r\n```\r\n\r\n#### useReducer\r\n\r\n`useReducer` and `useState` are almost the same，but `useReducer` needs a global reducer\r\n\r\n```js\r\nfunction reducer(state, action) {\r\n  switch (action.type) {\r\n    case 'up':\r\n      return { count: state.count + 1 }\r\n    case 'down':\r\n      return { count: state.count - 1 }\r\n  }\r\n}\r\n\r\nfunction App() {\r\n  const [state, dispatch] = useReducer(reducer, { count: 1 })\r\n  return (\r\n    \u003c\u003e\r\n      {state.count}\r\n      \u003cbutton onClick={() =\u003e dispatch({ type: 'up' })}\u003e+\u003c/button\u003e\r\n      \u003cbutton onClick={() =\u003e dispatch({ type: 'down' })}\u003e-\u003c/button\u003e\r\n    \u003c/\u003e\r\n  )\r\n}\r\n```\r\n\r\n#### useEffect\r\n\r\nIt is the execution and cleanup of effects, which is represented by the second parameter\r\n\r\n```\r\nuseEffect(f)       //  effect (and clean-up) every time\r\nuseEffect(f, [])   //  effect (and clean-up) only once in a component's life\r\nuseEffect(f, [x])  //  effect (and clean-up) when property x changes in a component's life\r\n```\r\n\r\n```js\r\nfunction App({ flag }) {\r\n  const [count, setCount] = useState(0)\r\n  useEffect(() =\u003e {\r\n    document.title = 'count is ' + count\r\n  }, [flag])\r\n  return (\r\n    \u003c\u003e\r\n      \u003ch1\u003e{count}\u003c/h1\u003e\r\n      \u003cbutton onClick={() =\u003e setCount(count + 1)}\u003e+\u003c/button\u003e\r\n    \u003c/\u003e\r\n  )\r\n}\r\n```\r\n\r\nIf it returns a function, the function can do cleanups:\r\n\r\n```js\r\nuseEffect(() =\u003e {\r\n  document.title = 'count is ' + count\r\n  return () =\u003e {\r\n    store.unsubscribe()\r\n  }\r\n}, [])\r\n```\r\n\r\n#### useLayout\r\n\r\nMore like useEffect, but useLayout is sync and blocking UI.\r\n\r\n```js\r\nuseLayout(() =\u003e {\r\n  document.title = 'count is ' + count\r\n}, [flag])\r\n```\r\n\r\n#### useMemo\r\n\r\n`useMemo` has the same rules as `useEffect`, but `useMemo` will return a cached value.\r\n\r\n```js\r\nconst memo = (c) =\u003e (props) =\u003e useMemo(() =\u003e c, [Object.values(props)])\r\n```\r\n\r\n#### useCallback\r\n\r\n`useCallback` is based `useMemo`, it will return a cached function.\r\n\r\n```js\r\nconst cb = useCallback(() =\u003e {\r\n  console.log('cb was cached.')\r\n}, [])\r\n```\r\n\r\n#### useRef\r\n\r\n`useRef` will return a function or an object.\r\n\r\n```js\r\nfunction App() {\r\n  useEffect(() =\u003e {\r\n    console.log(t) // { current:\u003cdiv\u003et\u003c/div\u003e }\r\n  })\r\n  const t = useRef(null)\r\n  return \u003cdiv ref={t}\u003et\u003c/div\u003e\r\n}\r\n```\r\n\r\nIf it uses a function, it can return a cleanup and executes when removed.\r\n\r\n```js\r\nfunction App() {\r\n  const t = useRef((dom) =\u003e {\r\n    if (dom) {\r\n      doSomething()\r\n    } else {\r\n      cleanUp()\r\n    }\r\n  })\r\n  return flag \u0026\u0026 \u003cspan ref={t}\u003eI will removed\u003c/span\u003e\r\n}\r\n```\r\n### Fragments\r\n\r\n```js\r\n// fragment\r\nfunction App() {\r\n  return \u003c\u003e{something}\u003c/\u003e\r\n}\r\n// render array\r\nfunction App() {\r\n  return [a, b, c]\r\n}\r\n```\r\n\r\n\r\n### jsx2\r\n\r\n```js\r\nplugins: [\r\n  [\r\n    '@babel/plugin-transform-react-jsx',\r\n    {\r\n      runtime: 'automatic',\r\n      importSource: 'fre',\r\n    },\r\n  ],\r\n]\r\n```\r\n\r\n### Compare with other frameworks\r\n\r\nThe comparison is difficult because the roadmap and trade-offs of each framework are different, but we have to do so.\r\n\r\n- react\r\n\r\nReact is the source of inspiration for fre. Their implementation and asynchronous rendering are similar. The most amazing thing is **concurrent mode**, which means that react and fre have the same roadmap -- **Exploring concurrent use cases**.\r\n\r\nBut at the same time, fre has obvious advantages in concurrent mode and bundle size.\r\n\r\n- vue / preact\r\n\r\nTo some extent, vue and preact are similar. They have similar synchronous rendering, only the API is different.\r\n\r\nThe reconciliation algorithm of fre is similar to vue2, but the biggest difference is that vue/preact do not support concurrent mode, this means that the roadmap is totally different.\r\n\r\n#### License\r\n\r\nMIT @yisar\r\n\r\n\r\n[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fyisar%2Ffre.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fyisar%2Ffre?ref=badge_large)\r\n \r\n","funding_links":["https://opencollective.com/fre","https://github.com/sponsors/yisar"],"categories":["TypeScript","UI Frameworks"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrejs%2Ffre","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrejs%2Ffre","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrejs%2Ffre/lists"}