{"id":20477895,"url":"https://github.com/hung-phan/koa-react-isomorphic","last_synced_at":"2026-03-16T20:31:52.760Z","repository":{"id":35093394,"uuid":"39256493","full_name":"hung-phan/koa-react-isomorphic","owner":"hung-phan","description":"Boilerplate for Koa \u0026 React","archived":false,"fork":false,"pushed_at":"2018-09-17T02:18:28.000Z","size":2742,"stargazers_count":127,"open_issues_count":1,"forks_count":16,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-04T09:11:40.349Z","etag":null,"topics":["boilerplate","immutablejs","koa","react","redux","relay","ssr","webpack"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/hung-phan.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}},"created_at":"2015-07-17T13:53:09.000Z","updated_at":"2023-11-07T12:40:09.000Z","dependencies_parsed_at":"2022-08-04T00:00:22.909Z","dependency_job_id":null,"html_url":"https://github.com/hung-phan/koa-react-isomorphic","commit_stats":null,"previous_names":[],"tags_count":191,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hung-phan%2Fkoa-react-isomorphic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hung-phan%2Fkoa-react-isomorphic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hung-phan%2Fkoa-react-isomorphic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hung-phan%2Fkoa-react-isomorphic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hung-phan","download_url":"https://codeload.github.com/hung-phan/koa-react-isomorphic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248717237,"owners_count":21150389,"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":["boilerplate","immutablejs","koa","react","redux","relay","ssr","webpack"],"created_at":"2024-11-15T15:34:07.344Z","updated_at":"2025-10-06T02:36:23.917Z","avatar_url":"https://github.com/hung-phan.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React and Koa boilerplate (deprecated. New project available at https://github.com/hung-phan/micro-nextjs)\n[![build status](https://secure.travis-ci.org/hung-phan/koa-react-isomorphic.svg)](http://travis-ci.org/hung-phan/koa-react-isomorphic/)\n[![codecov](https://codecov.io/gh/hung-phan/koa-react-isomorphic/branch/master/graph/badge.svg)](https://codecov.io/gh/hung-phan/koa-react-isomorphic)\n[![Dependency Status](https://david-dm.org/hung-phan/koa-react-isomorphic.svg)](https://david-dm.org/hung-phan/koa-react-isomorphic)\n[![devDependency Status](https://david-dm.org/hung-phan/koa-react-isomorphic/dev-status.svg)](https://david-dm.org/hung-phan/koa-react-isomorphic#info=devDependencies)\n\nThe idea of this repository is to implement all new concepts and libraries which work great for React.js.\n\n* [Koa.js](https://github.com/koajs/koa)\n* [Webpack](https://github.com/webpack/webpack)\n* [Babel](https://babeljs.io/)\n* [Flowtype](http://flowtype.org/)\n* [Marko](http://markojs.com/)\n* [Bootstrap](http://getbootstrap.com/css/) and [FontAwesome](https://fortawesome.github.io/Font-Awesome/)\n* [Redux](https://github.com/rackt/redux)\n* [Relay](https://facebook.github.io/relay/)\n* [Immutablejs](https://facebook.github.io/immutable-js/)\n* [ServiceWorker and AppCache](http://www.html5rocks.com/en/tutorials/service-worker/introduction/)\n* [PostCSS](https://github.com/postcss/postcss), [CSSNext](http://cssnext.io/), [CSSNano](http://cssnano.co/)\n* [Jest](https://facebook.github.io/jest), [Nock](https://github.com/pgte/nock)\n\n## Requirement\n- Install [redux-devtools-extension](https://github.com/zalmoxisus/redux-devtools-extension) to have better experience when developing.\n- Install [yarn](https://github.com/yarnpkg/yarn)\n\n## Features\n* Immutablejs: Available on [features/immutablejs](https://github.com/hung-phan/koa-react-isomorphic/tree/features/immutable-js)\n* Relay: Available on [features/relay](https://github.com/hung-phan/koa-react-isomorphic/tree/features/relay)\n\n## Explanation\n\n### Templates\nTemplates are written in marko.js with predefined template helpers. To see its usage, please refer to `layout/application.marko`.\n\n### Server side rendering\nI use [webpack-isomorphic-tools](https://github.com/halt-hammerzeit/webpack-isomorphic-tools) to support loading assets in\nthe server side. You can see the configuration file under `config` folder.\n\n#### Fetch data\n- For redux, data is fetched using redial hooks on the server side.\n\nTakes a look at `templates/todos`, I will have sth like:\n\n```javascript\n  createRedialEnhancer({\n    [FETCH_DATA_HOOK]: ({ store }) =\u003e store.dispatch(fetchTodos()),\n    [UPDATE_HEADER_HOOK]: ({ store }) =\u003e store.dispatch(updateLink([\n      // window.javascriptAssets will be injected to do preload link for optimizing route\n      { rel: 'preload', href: window.javascriptAssets['static-page'], as: 'script' },\n    ])),\n  })\n```\n\n- For relay, data is fetched using isomorphic-relay-router on the server side.\n\n### Default require for node\nThe default `require` node statement has been modified by webpack-isomorphic-tools, so I remap it with `nodeRequire`\nunder `global`. For example, you can use it like as below:\n\n```javascript\nconst { ROOT, PUBLIC } = global.nodeRequire('./config/path-helper');\n```\n\nNote: `nodeRequire` will resolve the path from project root directory.\n\n\n#### Preload assets via redial\nTo be able to support for asynchronous chunks loading using `\u003clink rel='preload' ... /\u003e`, I returned the javascript\nassets map for all the routes to the client via `window.javascriptAssets`.\n\nYou can use this to inject assets for the next page to improve performance. This is what I am trying to achieve\n[preload-webpack-plugin](https://github.com/GoogleChrome/preload-webpack-plugin).\n\nThis will map the hook with the current component and trigger it (Note: This will only be applied to root component).\n\n### Async react components\n[react-loadable](https://github.com/thejameskyle/react-loadable)\n\n### Idea to structure redux application\nFor now, the best way is to place all logic in the same place with components to make it less painful when scaling the application.\nCurrent structure is the combination of ideas from [organizing-redux](http://jaysoo.ca/2016/02/28/organizing-redux-application/) and\n[ducks-modular-redux](https://github.com/erikras/ducks-modular-redux). Briefly, I will have our reducer, action-types, and actions\nin the same place with featured components.\n\n![alt text](https://raw.githubusercontent.com/hung-phan/koa-react-isomorphic/master/redux-structure.png \"redux structure\")\n\n#### Localize selectors\nSome great ideas from [scoped-selectors-for-redux-modules](http://www.datchley.name/scoped-selectors-for-redux-modules/).\nYou can create a localized scope for selector using `globalizeSelectors`.\n\n\n```javascript\nexport const mountPoint = 'todos';\n\nexport const selectors = globalizeSelectors({\n  getTodos: identity, // it will also work with reselect library\n}, mountPoint);\n```\n\nThen in main reducer, you can have sth like this, which helps reduce the coupling with React view\n\n```javascript\n/* @flow */\nimport { combineReducers } from 'redux';\nimport todosReducer, { mountPoint as todosMountPoint } from './components/todos/logicBundle';\nimport routingReducer, { mountPoint as routingMountPoint } from './components/routing/logicBundle';\nimport helmetReducer, { mountPoint as helmetMountPoint } from './components/helmet/logicBundle';\n\nexport default combineReducers({\n  [todosMountPoint]: todosReducer,\n  [routingMountPoint]: routingReducer,\n  [helmetMountPoint]: helmetReducer,\n});\n```\n\nSample for logicBundle:\n\n```javascript\nexport const mountPoint = \"todos\";\n\nexport const selectors = globalizeSelectors(\n  {\n    getTodos: identity\n  },\n  mountPoint\n);\n\nexport const ADD_TODO = \"todos/ADD_TODO\";\nexport const REMOVE_TODO = \"todos/REMOVE_TODO\";\nexport const COMPLETE_TODO = \"todos/COMPLETE_TODO\";\nexport const SET_TODOS = \"todos/SET_TODOS\";\n\nexport const addTodo: AddTodoActionType = createAction(ADD_TODO);\nexport const removeTodo: RemoveTodoActionType = createAction(REMOVE_TODO);\nexport const completeTodo: CompleteTodoActionType = createAction(COMPLETE_TODO);\nexport const setTodos: SetTodosActionType = createAction(SET_TODOS);\nexport const fetchTodos = () =\u003e\n  (dispatch: Function): Promise\u003cTodoType[]\u003e =\u003e\n    fetch(getUrl(\"/api/v1/todos\"))\n      .then(res =\u003e res.json())\n      .then((res: TodoType[]) =\u003e dispatch(setTodos(res)));\n\nexport default handleActions(\n  {\n    [ADD_TODO]: (state, { payload: text }) =\u003e update(state, {\n      $push: [{ text, complete: false }]\n    }),\n    [REMOVE_TODO]: (state, { payload: index }) =\u003e update(state, {\n      $splice: [[index, 1]]\n    }),\n    [COMPLETE_TODO]: (state, { payload: index }) =\u003e update(state, {\n      $splice: [\n        [index, 1],\n        [index, 0, { ...state[index], complete: !state[index].complete }]\n      ]\n    }),\n    [SET_TODOS]: (state, { payload: todos }) =\u003e todos\n  },\n  []\n);\n```\n\n## Upcoming\n* Phusion Passenger server with Nginx\n\n## Development\n\n```bash\n$ git clone git@github.com:hung-phan/koa-react-isomorphic.git\n$ cd koa-react-isomorphic\n$ yarn install\n```\n\n### Hot reload\n\n```bash\n$ yarn run watch\n$ yarn run dev\n```\n\n### With server rendering - encourage for testing only\n\n```bash\n$ SERVER_RENDERING=true yarn run watch\n$ yarn run dev\n```\n\n### Enable flowtype in development\n```bash\n$ yarn run flow-watch\n$ yarn run flow-stop # to terminate the server\n```\n\nYou need to add annotation to the file to enable flowtype (`// @flow`)\n\n\n## Test\n\n```bash\n$ yarn test\n```\n\n## Debug\n```bash\n$ yarn run watch\n$ yarn run debug\n```\n\n## Production\n\n### Start production server\n\n```bash\n$ yarn run build\n$ SECRET_KEY=your_env_key yarn start\n```\n\n### Docker container\n\n```bash\n$ docker-compose build\n$ docker-compose up\n```\n\nAccess `http://localhost:3000` to see the application\n\n## QA\n\nFeel free to open an issue on the repo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhung-phan%2Fkoa-react-isomorphic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhung-phan%2Fkoa-react-isomorphic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhung-phan%2Fkoa-react-isomorphic/lists"}