{"id":13799078,"url":"https://github.com/artdecocode/koa2-jsx","last_synced_at":"2026-01-04T22:06:06.031Z","repository":{"id":135405155,"uuid":"124941815","full_name":"artdecocode/koa2-jsx","owner":"artdecocode","description":"A Koa2 middleware to render JSX templates with Redux support","archived":false,"fork":false,"pushed_at":"2018-05-22T00:39:35.000Z","size":285,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-01T00:08:28.416Z","etag":null,"topics":["jsx","koa2","koa2-middleware","react"],"latest_commit_sha":null,"homepage":null,"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/artdecocode.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":"2018-03-12T19:40:33.000Z","updated_at":"2018-05-22T00:39:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"56545ea4-87c5-42f1-987d-30f2809a320a","html_url":"https://github.com/artdecocode/koa2-jsx","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fkoa2-jsx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fkoa2-jsx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fkoa2-jsx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artdecocode%2Fkoa2-jsx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/artdecocode","download_url":"https://codeload.github.com/artdecocode/koa2-jsx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244313827,"owners_count":20433011,"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":["jsx","koa2","koa2-middleware","react"],"created_at":"2024-08-04T00:00:58.610Z","updated_at":"2026-01-04T22:06:05.946Z","avatar_url":"https://github.com/artdecocode.png","language":"JavaScript","funding_links":[],"categories":["仓库"],"sub_categories":["中间件"],"readme":"# koa2-jsx\n\n[![npm version](https://badge.fury.io/js/koa2-jsx.svg)](https://badge.fury.io/js/koa2-jsx)\n\n```bash\nyarn add koa2jsx\n```\n\n`koa2-jsx` is _middleware_ for [`Koa2`][3] which provides support for rendering JSX in a server application via `react-dom/server`. It also uses `Redux` to create a store updatable with actions for a better control of what data needs to be rendered, for example, it is possible to create a reducer with a `title` slice, and an action to set that property from `ctx`, and then print `{ title }` in the JSX template.\n\nIn addition to the core functionality, the package gives a minimum wireframe `View` container and actions to set page title and viewport, add external and inline scripts and styles, icons and a link to the manifest file.\n\nFinally, there's an extra middleware function which can be used after `koa2-jsx` and wireframe actions were installed to include links to `Bootstrap 4` scripts (including jQuery) and CSS.\n\n## API\n\nThe module will return a single middleware function which accepts 3 arguments: `reducer`, `View` and `actions`. They are describe below in the [_Example section_](#example).\n\nThe middleware function will perform the following for each request:\n\n1. Initialise the Redux store by creating a **reducer**;\n1. assign **actions** to the context, such as `{ setTitle(title) }` becomes `ctx.setTitle`;\n1. wait for all other middleware and pages to resolve; _and_\n1. render `ctx.Content` if found using `react-dom/server` as a stream with doctype html sent, using the **View**.\n\n```jsx\nimport Koa from 'koa2'\nimport koa2Jsx from 'koa2-jsx'\nimport { combineReducers } from 'redux'\nimport { connect } from 'react-redux'\n\nconst app = new Koa()\n\nconst View = ({ title, children }) =\u003e {\n  return (\n    \u003chtml lang=\"en\"\u003e\n      \u003chead\u003e\n        \u003ctitle\u003e{title}\u003c/title\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        {children}\n      \u003c/body\u003e\n    \u003c/html\u003e\n  )\n}\n\nconst jsx = koa2Jsx({\n  reducer: combineReducers({\n    title(state = null, { type, title }) {\n      if (type != 'SET_TITLE') return state\n      return title\n    }\n  })\n  actions: {\n    setTitle(title) {\n      return { type: 'SET_TITLE', title }\n    }\n  }\n  View: connect(state =\u003e state)(View),\n})\n\napp.use(jsx, async (ctx, next) =\u003e {\n  ctx.setTitle('Welcome to the koa2-jsx world')\n  ctx.Content = \u003cdiv\u003e\u003ch1\u003e Hello @ there \u003c/h1\u003e\u003c/div\u003e\n  await next()\n})\n```\n\nWhen setting up middleware, ensure that the `koa2-jsx` middleware function comes\nahead of pages so that the Redux store and render logic are initialised.\n\nIf `ctx.Content` is set in downstream application middleware, `\u003c!doctype html\u003e`\nis written and a readable stream from React Dom's\n`renderToStaticNodeStream(\u003cWebPage /\u003e)` is be piped into `ctx.body`.\n\n### `koa2Jsx({`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`reducer: function,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`View: Container,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`actions: object,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`static?: boolean = true,`\u003cbr/\u003e\u0026nbsp;\u0026nbsp;`render?: function,`\u003cbr/\u003e`}): function`\n\nThis will set up the middleware function and return it. Add it as a usual `Koa2` middleware (shown below).\n\n### Example\n\nThe example shows how to create a reducer, actions and View for a minimum HTML\ntemplate.\n\n```js\n/* yarn example/ */\nimport Koa from 'koa2'\nimport koa2Jsx from 'koa2-jsx'\nimport actions from './actions'\nimport reducer from './reducer'\nimport View from './Containers/View'\n\nconst app = new Koa()\n\nconst jsx = koa2Jsx({\n  reducer,\n  View,\n  actions,\n  static: true,\n  // ^ set to false for universal applications\n  pretty: false,\n  // ^ set to true for prettified HTML output\n})\n\napp.use(jsx, async (ctx, next) =\u003e {\n  ctx.setTitle('Welcome to the koa2-jsx world')\n  ctx.Content = \u003cdiv\u003e\u003ch1\u003eHello @ there\u003c/h1\u003e\u003c/div\u003e\n  await next()\n})\n```\n\n### reducer\n\nThe reducer is either a simple function or a combination of reducers created with `combineReducers` from the `redux` package. The reducer is used during the initialisation of the middleware to create a _store_ with `createStore(reducer)`. The _store_ is used in rendering as a context for the _View_ container. This way, it's possible to pass data to the template by invoking methods on the Koa's context (see actions).\n\n```js\nimport { combineReducers } from 'redux'\n\nconst title = (state = null, { type, title }) =\u003e {\n  if (type != 'SET_TITLE') return state\n  return title\n}\n\nexport default combineReducers({\n  title,\n})\n```\n\n### View\n\nThe view can be a connected `react-redux` component when actions and a reducer are used, or a pure `React` component when they're omitted. It follows the same principles as when developing for a browser-side `react-redux` application, so that it accepts the state of the reducer as the first argument, with `children` property (set to `ctx.Content`).\n\n```js\nimport { connect } from 'react-redux'\n\nconst View = ({ title, children }) =\u003e {\n  return (\n    \u003chtml lang=\"en\"\u003e\n      \u003chead\u003e\n        \u003ctitle\u003e{title}\u003c/title\u003e\n      \u003c/head\u003e\n      \u003cbody\u003e\n        {children}\n      \u003c/body\u003e\n    \u003c/html\u003e\n  )\n}\n\nexport default connect(state =\u003e state)(View)\n```\n\n### actions\n\nActions map action creators to Koa's context, so that it is possible to dispatch\nactions from `ctx` to control the state and thus data which goes into the\ntemplate.\n\n```js\nconst actions = {\n  setTitle: title =\u003e ({ type: 'SET_TITLE', title }),\n  // ^ exported as ctx.setTitle(title)\n}\n\nexport default actions\n```\n\n### `static: bool = true`\n\nWhether to use static rendering, i.e., without React's metadata required for hydration on the client-side. Set to `false` when building universal applications.\n\nResults with static:\n\n```html\n\u003cdiv class=\"container\"\u003e\u003cdiv class=\"row\"\u003e\u003cdiv class=\"col\"\u003etest\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n```\n\nand without static:\n\n```html\n\u003cdiv class=\"container\" data-reactroot=\"\"\u003e\u003cdiv class=\"row\"\u003e\u003cdiv class=\"col\"\u003etest\u003c/div\u003e\u003c/div\u003e\u003c/div\u003e\n```\n\n### `pretty: bool = false`\n\nPrettify HTML output. This will use string rendering to get HTML before formatting, therefore it's slower to display a page.\n\n```html\n\u003cdiv class=\"container\" data-reactroot=\"\"\u003e\n    \u003cdiv class=\"row\"\u003e\n        \u003cdiv class=\"col\"\u003e\n            \u003cp\u003eTest\u003c/p\u003e\n        \u003c/div\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n```\n\n### `render: function(ctx: Koa.Context, WebSite: React.Component)`\n\nIt is possible to pass a custom render function. You should implement your own render for more control when needed. It accepts a Koa's context and a `WebSite` arguments. The `WebSite` is a `View` container wrapped in a state provider.\n\nExamples below show how you can implement (a) markup renderer:\n\n```js\nimport { renderToStaticMarkup } from 'react-dom/server'\nimport { prettyPrint } from 'html'\n\nconst render = (ctx, WebSite) =\u003e {\n  ctx.type = 'html'\n  ctx.status = 200\n  ctx.res.write('\u003c!doctype html\u003e\\n')\n  const markup = renderToStaticMarkup(WebSite)\n  const s = prettyPrint(markup)\n  ctx.body = s\n}\n```\n\n(b) stream renderer:\n\n```js\nimport { renderToStaticNodeStream } from 'react-dom/server'\n\nconst streamRender = (ctx, WebSite) =\u003e {\n  ctx.type = 'html'\n  ctx.status = 200\n  ctx.res.write('\u003c!doctype html\u003e\\n')\n  const stream = renderToStaticNodeStream(WebSite)\n  ctx.body = stream\n}\n```\n\n## Wireframe\n\nThe wireframe provides a `reducer`, `actions` and `View` to be used when creating web pages. It accounts for most common use cases, such as assigning viewport and icons. To include it in your application, use:\n\n```js\nimport koa2Jsx, { wireframe } from 'koa2-jsx'\n\nconst jsx = koa2Jsx(wireframe)\n\n/* or using object destructuring */\n\nconst jsx = koa2Jsx({\n  ...wireframe,\n  pretty: true,\n})\n```\n\n### Template\n\nThe following template is used, which allows to set viewport, title, add links, external scripts and script and style blocks.\n\n```html\n\u003chtml lang=\"en\"\u003e\n  \u003chead\u003e\n    \u003cmeta charSet=\"utf-8\" /\u003e\n    {viewport \u0026\u0026\n      \u003cmeta name=\"viewport\" content={viewport} /\u003e\n    }\n\n    \u003ctitle\u003e{title}\u003c/title\u003e\n\n    \u003c!-- css, icons, manifest --\u003e\n    {links.map((props, i) =\u003e\n      \u003clink key={i} {...props} /\u003e\n    )}\n    \u003c!-- CSS --\u003e\n    {styles.map((style, i) =\u003e\n      \u003cstyle key={i} dangerouslySetInnerHTML={{ __html: style }} /\u003e\n    )}\n  \u003c/head\u003e\n  \u003cbody\u003e\n    {children}\n\n    {scripts.map((props, i) =\u003e\n      \u003cscript key={i} {...props} /\u003e\n    )}\n    {js.map((script, i) =\u003e\n      \u003cscript key={i} dangerouslySetInnerHTML={{ __html: script }} /\u003e\n    )}\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Actions\n\nTo update the data to present in the template, the actions API is as follows.\n\n### `setTitle(title)`\n\nSet title of the page.\n\n```js\nctx.setTitle('koa2-jsx')\n```\n\n```html\n\u003ctitle\u003ekoa2-jsx\u003c/title\u003e\n```\n\n### `setViewport(viewport)`\n\nSet the viewport.\n\n```js\nctx.setViewport('width=device-width, initial-scale=1, shrink-to-fit=no')\n```\n\n```html\n\u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" /\u003e\n```\n\n\n### `addManifest(href)`\n\nAdd a link to the manifest file.\n\n```js\nctx.addManifest('/manifest.json')\n```\n\n```html\n\u003clink rel=\"manifest\" href=\"/manifest.json\" /\u003e\n```\n\n### `addIcon(href | [[href, type, sizes, ref=icon]])`\n\nAdd an icon or icons links.\n\n```js\nctx.addIcon('/icons/favicon.ico')\nctx.addIcon([\n  [\n    '/icons/favicon-32x32.png',\n    'image/png',\n    '32x32',\n  ],\n  [\n    '/icons/apple-icon-180x180.png',\n    'image/png',\n    '180x180',\n    'apple-touch-icon',\n  ],\n])\n```\n\n```html\n\u003clink href=\"/icons/favicon.ico\" rel=\"icon\" /\u003e\n\u003clink href=\"/icons/favicon-32x32.png\" type=\"image/png\" sizes=\"32x32\" rel=\"icon\" /\u003e\n\u003clink href=\"/icons/apple-icon-180x180.png\" type=\"image/png\" sizes=\"180x180\" rel=\"apple-touch-icon\" /\u003e\n```\n\n### `addScript(src | [[src, integrity, crossOrigin]])`\n\nAdd a single, or multiple script tags. If integrity and origin need to be used,\nan array must be passed.\n\n```js\nctx.addScript('/js/bundle.js')\nctx.addScript([\n  [\n    'https://code.jquery.com/jquery-3.2.1.slim.min.js',\n    ...(process.env.NODE_ENV == 'production' ? [\n      'sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN',\n      'anonymous',\n    ] : []),\n  ],\n])\n```\n\n```html\n\u003cscript src=\"/js/bundle.js\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://code.jquery.com/jquery-3.2.1.slim.min.js\" integrity=\"sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN\" crossorigin=\"anonymous\"\u003e\u003c/script\u003e\n```\n\n### `addCss(href | [[href, integrity, crossOrigin]])`\n\nAdd a single, or multiple style links. If integrity and origin need to be\nspecified, an array must be passed.\n\n```js\nctx.addCss('https://fonts.googleapis.com/css?family=Roboto:700\u0026effect=anaglyph|3d-float')\nctx.addCss([\n  [\n    'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css',\n    ...(process.env.NODE_ENV == 'production' ? [\n      'sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm',\n      'anonymous',\n    ] : []),\n  ],\n])\n```\n\n```html\n\u003clink href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css\" integrity=\"sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm\" crossorigin=\"anonymous\" rel=\"stylesheet\" /\u003e\n\u003clink href=\"https://fonts.googleapis.com/css?family=Roboto:700\u0026amp;effect=anaglyph|3d-float\" rel=\"stylesheet\" /\u003e\n```\n\n### `addStyle(style)`\n\nAdd a style block to the HTML.\n\n```js\nctx.addStyle('h1 { font-family: \\'Roboto\\', sans-serif; }')\n```\n\n```html\n\u003cstyle\u003e\n    h1 { font-family: 'Roboto', sans-serif; }\n\u003c/style\u003e\n```\n\n### `addJs(js)`\n\nAdd a block of JS code.\n\n```js\nctx.addJs('$(\".alert\").alert()')\n```\n\n```html\n\u003cscript\u003e\n    $(\".alert\").alert()\n\u003c/script\u003e\n```\n\n## Bootstrap\n\nTo include the full `Bootstrap 4` support to an HTML page, use the following snippet:\n\n```js\nimport koa2Jsx, { bootstrap, wireframe } from 'koa2-jsx'\n\nconst jsx = koa2Jsx(wireframe)\n\n// ...\napp.use(jsx)\napp.use(bootstrap)\n```\n\n```html\n\u003c!-- viewport is assigned --\u003e\n\u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\" /\u003e\n\n\u003c!-- css embedded --\u003e\n\u003clink href=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css\" integrity=\"sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm\" origin=\"anonymous\" rel=\"stylesheet\" /\u003e\n\n\u003c!-- script tags included --\u003e\n\u003cscript src=\"https://code.jquery.com/jquery-3.2.1.slim.min.js\" integrity=\"sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN\" origin=\"anonymous\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js\" integrity=\"sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q\" origin=\"anonymous\"\u003e\u003c/script\u003e\n\u003cscript src=\"https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js\" integrity=\"sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl\" origin=\"anonymous\"\u003e\u003c/script\u003e\n```\n\n## Babel\n\nTo start using `koa2-jsx`, you really need to be able to write `JSX` syntax.\nDuring development, use `@babel/register`, and for production compile your\nproject. You can of course create classes with `create-react-class`\n(see [reading](#reading)) and then not require babel transpilation but the\npoint of this package is to write `JSX` which is unfortunately is not native to\n_Node.JS_.\n\n### Development\n\n`@babel/register` is a good way to develop with koa and `koa2-jsx`, however\nwhen using in production, it is recommended to build the app.\n\n```js\nrequire('@babel/register')\nrequire('.')\n```\n\n### .babelrc\n\nTo build JSX code, you can use the following `.babelrc` snippet:\n\n```json\n{\n  \"plugins\": [\n    \"react-require\",\n    \"@babel/plugin-syntax-object-rest-spread\",\n    \"@babel/plugin-transform-modules-commonjs\"\n  ],\n  \"presets\": [\n    \"@babel/preset-react\"\n  ]\n}\n```\n\n\u003c!-- ## Project structure\n\nYou can use `containers` and `components` folders and put tests in\nthere as well.\n\nThe routes, if you're using a router, routes will import the jsx\npages, and do `ctx.Content = \u003cPage/\u003e; await next()` to render a page.\n\n```sh\n- src\n  - Components\n    - View.jsx\n  - Containers\n    - View.jsx\n    - View.test.jsx\n  - Pages\n    - Index.jsx\n  - routes\n    - index.js\n``` --\u003e\n\n## Using with [idio][2]\n\nThe `koa2-jsx` middlware comes with the Koa2-based framework [`idio`][2] which\napart from other pre-installed middleware such as sessions and Mongo support\nout of the box, also provides automatic routes initialisation from a given\nfolder and hot route reload.\n\nWith `koa2-jsx`, `idio` allows to [use JSX with Koa2][2] for templates. It's\nvery powerful and can be used in isomorphic applications.\n\n## Reading\n\n- [Redux Server Rendering][4] explains how to render pages with a store\nserver-side, and send initial data to the browser.\n- [React without ES6][5] talks about how to use `create-react-class` to create\ninstances of components, that is not using `jsx` syntax.\n\n---\n\n(c) [Art Deco Code][1] 2018\n\n[1]: https://adc.sh\n[2]: https://idio.cc\n[3]: http://koajs.com\n[4]: https://redux.js.org/recipes/server-rendering\n[5]: https://reactjs.org/docs/react-without-es6.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartdecocode%2Fkoa2-jsx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fartdecocode%2Fkoa2-jsx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartdecocode%2Fkoa2-jsx/lists"}