{"id":18608509,"url":"https://github.com/iansinnott/react-static-webpack-plugin","last_synced_at":"2025-04-09T13:04:11.981Z","repository":{"id":57345341,"uuid":"48868635","full_name":"iansinnott/react-static-webpack-plugin","owner":"iansinnott","description":"Build full static sites using React, React Router and Webpack (Webpack 2 supported)","archived":false,"fork":false,"pushed_at":"2020-06-02T23:46:56.000Z","size":557,"stargazers_count":155,"open_issues_count":38,"forks_count":24,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-02T12:13:32.020Z","etag":null,"topics":[],"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/iansinnott.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":"2016-01-01T03:36:24.000Z","updated_at":"2024-07-19T20:26:09.000Z","dependencies_parsed_at":"2022-09-17T06:31:32.842Z","dependency_job_id":null,"html_url":"https://github.com/iansinnott/react-static-webpack-plugin","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iansinnott%2Freact-static-webpack-plugin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iansinnott%2Freact-static-webpack-plugin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iansinnott%2Freact-static-webpack-plugin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iansinnott%2Freact-static-webpack-plugin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iansinnott","download_url":"https://codeload.github.com/iansinnott/react-static-webpack-plugin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248045230,"owners_count":21038553,"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-11-07T03:03:14.610Z","updated_at":"2025-04-09T13:04:11.938Z","avatar_url":"https://github.com/iansinnott.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Static Webpack Plugin\n\n[![Build Status](https://img.shields.io/circleci/project/iansinnott/react-static-webpack-plugin.svg)](https://circleci.com/gh/iansinnott/react-static-webpack-plugin)\n[![react-static-webpack-plugin on NPM](https://img.shields.io/npm/v/react-static-webpack-plugin.svg)](https://www.npmjs.com/package/react-static-webpack-plugin)\n\n\n_Build full static sites using React, React Router and Webpack_\n\n\u003e This module can be added to exiting projects, but if you're looking to start coding right now check out the [React Static Boilerplate][boilerplate].\n\n## Install\n\n```\n$ npm install --save-dev react-static-webpack-plugin\n```\n\n## Usage (Webpack 2.x)\n\nThis plugin should Just Work™ with Webpack 2, so have a look at the examples below. However, some of them may contain other configuration which is specific to Webpack 1, so your best bet if something doesn't work is to check out the:\n\n👉 **[Webpack 2 Example][]**\n\n[Webpack 2 Example]: https://github.com/iansinnott/react-static-webpack-plugin/tree/master/example/webpack-2\n\n## Usage (Webpack 1.x)\n\n### Simple Example\n\n```js\n// webpack.config.js\nconst ReactStaticPlugin = require('react-static-webpack-plugin');\n\nmodule.exports = {\n\n  entry: {\n    app: './client/index.js',\n  },\n\n  output: {\n    path: path.join(__dirname, 'public'),\n    filename: '[name].js',\n    publicPath: '/',\n  },\n\n  plugins: [\n    new ReactStaticPlugin({\n      routes: './client/index.js',  // Path to routes file\n      template: './template.js',    // Path to JSX template file\n    }),\n  ],\n\n  // ... other config\n\n};\n```\n\n```js\n// client/index.js\nimport React from 'react';\nimport { render } from 'react-dom';\nimport App from './components/App.js';\n\nrender(\u003cApp /\u003e, document.getElementById('root'));\n\n// Be sure to export the React component so that it can be statically rendered\nexport default App;\n```\n\nNow when you run `webpack` you will see `index.html` in the output. Serve it statically and open it in any browser.\n\n### Multi-page sites with React Router\n\nCreating sites with multiple static pages using React Router is very similar to the simple example, but instead of exporting any old React component export a `\u003cRoute /\u003e` component:\n\n```js\n// client/index.js\nimport React from 'react';\nimport { render } from 'react-dom';\nimport { Router, browserHistory } from 'react-router';\n\n// Since we're rendering static files don't forget to use browser history.\n// Server's don't get the URL hash during a request.\nimport createBrowserHistory from 'history/lib/createBrowserHistory';\n\n// Import your routes so that you can pass them to the \u003cRouter /\u003e component\nimport routes from './routes.js';\n\nrender(\n  \u003cRouter routes={routes} history={browserHistory} /\u003e,\n  document.getElementById('root')\n);\n```\n\n```js\n// client/routes.js\nimport React from 'react';\nimport { Route } from 'react-router';\n\nimport {\n  App,\n  About,\n  Products,\n  Product,\n  Contact,\n  Nested,\n} from './components';\n\nconst NotFound = () =\u003e \u003ch4\u003eNot found 😞\u003c/h4\u003e;\n\nexport const routes = (\n  \u003cRoute path='/' title='App' component={App}\u003e\n    \u003cRoute path='about' title='App - About' component={About} /\u003e\n    \u003cRoute path='contact' title='App - Contact' component={Contact} /\u003e\n    \u003cRoute path='products' title='App - Products' component={Products}\u003e\n      \u003cRoute path='product' title='App - Products - Product' component={Product}\u003e\n        \u003cRoute path='nested' title='App - Products - Product - Nested' component={Nested} /\u003e\n      \u003c/Route\u003e\n    \u003c/Route\u003e\n    \u003cRoute path='*' title='404: Not Found' component={NotFound} /\u003e\n  \u003c/Route\u003e\n);\n\nexport default routes;\n```\n\n**NOTE:** The `title` prop on the `\u003cRoute /\u003e` components is totally optional but recommended. It will not affect your client side app, only the `\u003ctitle\u003e` tag of the generated static HTML.\n\nNow you will see nested HTML files int the `webpack` output. Given our router example it would look something like this:\n\n```\n                     Asset       Size  Chunks             Chunk Names\n                index.html  818 bytes          [emitted]\n                    app.js     797 kB       0  [emitted]  app\n                   app.css    8.28 kB       0  [emitted]  app\n                about.html    1.05 kB          [emitted]\n              contact.html    1.46 kB          [emitted]\n             products.html    2.31 kB          [emitted]\n      products/zephyr.html    2.45 kB          [emitted]\nproducts/zephyr/nomad.html    2.53 kB          [emitted]\n                  404.html  882 bytes          [emitted]\n```\n\n**NOTE:** When the plugin encounters `\u003cRoute path='*' /\u003e` it will assume that this is the 404 page and will name it `404.html`.\n\n### Full Example\n\nFor a full examples you can run locally, see the [`example/` directory](example) or the [React Static Boilerplate][boilerplate].\n\n## Generating `index.html` for every route\n\nBy default this plugin will generate a named HTML file for leaf routes, i.e. any route without child routes. Example:\n\n```js\n\u003cRoute path='about' component={About} /\u003e\n// -\u003e 'about.html'\n```\n\nHowever you can also chose to opt in to generating an `index.html` file for every route by simply adding a trailing `/` to your `path` prop. Example:\n\n```js\n// Notice the trailing slash below\n//                ↓\n\u003cRoute path='about/' component={About} /\u003e\n// -\u003e 'about/index.html'\n```\n\nSee the [deep route nesting](https://github.com/iansinnott/react-static-webpack-plugin/blob/master/example/deep-route-nesting/src/routes.js) example for a complete example of generating `index.html` files.\n\n## Rendering State React Components (Sort of like a browser)\n\nThis plugin uses [JSDOM][] to render your components in a pseudo browser environment. This means that everything you expect in the browser _should_ be available to you at render time. This means that code like this won't break your build:\n\n```js\nclass Comp extends React.Component {\n  constructor() {\n    this.width = window.innerWidth;\n    this.height = window.innerHeight;\n  }\n\n  render() {\n    const { width, height } = this;\n    return (\n      \u003cdiv style={{ width, height, }} className='Comp' /\u003e\n    );\n  }\n}\n```\n\nSince JSDOM provides a `window` object the React component above will be able to access the global window object just fine.\n\n[JSDOM]: https://github.com/tmpvar/jsdom\n\n## Current Limitations\n\nThis plugin does not currently support all the functionality of react router.\nMost notably it does not support dynamic route paths. For example:\n\n```js\n\u003cRoute path='blog' component={Blog}\u003e\n  \u003cRoute path=':id' component={Post} /\u003e\n\u003c/Route\u003e\n```\n\nIn a standard single page app when you hit the `Post` component you would probably look at the ID in the URL and fetch the appropriate post. However, to build static files we need all data available to us at the time of compilation, and in this case I have yet to come up with a clever way of passing dynamic data to the plugin and correctly mapping it to HTML files.\n\nI have some thoughts on this and am actively exploring how it might work but nothing has been implemented yet. If you have any thoughts on what this might look like please [open an issue][issues] and let me know!\n\n[issues]: https://github.com/iansinnott/react-static-webpack-plugin/issues\n\n## API\n\n### `new ReactStaticPlugin({ ...options })`\n\n#### `routes` (required)\n\n**Type:** `string`\n\nThe path to your routes component. Your routes component should be exported either as `routes` or the default: `'./client/routes.js'`\n\n#### `template` (required)\n\n**Type:** `string`\n\nPath to the file that exports your template React component. Example: `./template.js`\n\nWith this option you can provide the path to a custom React component that will render the layout for your static pages. The function will be passed an options object that will give you access to the page title and the rendered component:\n\n```js\n// template.js\nimport React from 'react';\n\nconst Html = (props) =\u003e (\n  \u003chtml lang='en'\u003e\n    \u003chead\u003e\n      \u003cmeta charSet='utf-8' /\u003e\n      \u003cmeta httpEquiv='X-UA-Compatible' content='IE=edge' /\u003e\n      \u003cmeta name='viewport' content='width=device-width, minimum-scale=1.0' /\u003e\n      \u003ctitle\u003e{props.title}\u003c/title\u003e\n      \u003cscript dangerouslySetInnerHTML={{ __html: 'console.log(\"analytics\")' }} /\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n      \u003cdiv id='root' dangerouslySetInnerHTML={{ __html: props.body }} /\u003e\n      \u003cscript src='/app.js' /\u003e\n    \u003c/body\u003e\n  \u003c/html\u003e\n);\n\nexport default Html;\n```\n\n**NOTE:** Your template component will be run through Webpack using whatever transformations or loaders you already have set up for the filetype specified. For example, if you are using babel for all JS files then your template file will be run through babel using whatever settings you have set up in `.babelrc`.\n\n**NOTE:** You can pass arbitrary data to your template component by adding to the options object passed when you initialize the plugin:\n\n```js\nnew ReactStaticPlugin({\n  routes: './client/index.js',\n  template: './template.js',\n\n  // Some arbitrary data...\n  someData: 'Welcome to Webpack plugins',\n}),\n```\n\nThen access the data within your template component using `props`:\n\n```js\n// template.js\nimport React from 'react';\n\nconst Html = (props) =\u003e (\n  \u003chtml lang='en'\u003e\n    \u003chead\u003e\n      \u003ctitle\u003e{props.title}\u003c/title\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n      \u003ch1\u003e{props.someData}\u003c/h1\u003e\n      \u003cdiv id='root' dangerouslySetInnerHTML={{ __html: props.body }} /\u003e\n      \u003cscript src='/app.js' /\u003e\n    \u003c/body\u003e\n  \u003c/html\u003e\n);\n\nexport default Html;\n```\n\nThe `props` object will have everything you passed in the options object to the plugin as well as:\n\n* `body`: A string of HTML to be rendered in the document.\n* `title`: A string that is passed from each of your Route components\n* `manifest`: An object mapping asset names to their generated output filenames. This will simply map asset names to themselves unless you add the [webpack-manifest-plugin][]. Example usage: `manifest['app.js']`. See the section on the [`manifest`](#manifest) option below.\n* `initialState`: If you pass the `reduxStore` option you will get access to the result of calling `store.getState()`. **NOTE:** Since this plugin makes no assumptions about the shape of your app state it is up to you to stringify it and place it in the DOM if you wish to use it.\n\n#### `reduxStore`\n\n**Type:** `string`\n\n**Default:** undefined\n\nThe path to your Redux store. This option allows you to pass a store to react-static-webpack-plugin. This allows for Redux support. The store you pass in will be used in tandem with the react-redux `\u003cProvider store={store}\u003e` component to render your Redux app to a static site.\n\n#### `renderToStaticMarkup`\n\n**Type:** `boolean`\n\n**Default:** `false`\n\nSet to `true` to use render output code without extra DOM attributes such as `data-reactid`, that React uses internally. This is useful if you want to use the React Static Webpack Plugin as a simple static page generator, as stripping away the extra attributes can save lots of bytes.\n\n#### `manifest`\n\n**Type:** `string`\n\n**Default:** `'manifest.json'`\n\n(Optional) The output filename of a manifest file if using the [webpack-manifest-plugin][]. This is useful if you want to have access to the manifest within your template file so that you can easily implement long-term caching.\n\n**IMPORTANT NOTE:** For this to work you _MUST_ include the webpack-manifest-plugin _before_ this static site plugin.\n\nExample:\n\n```js\n// webpack.config.js\nmodule.exports = {\n\n  // Other config...\n\n  plugins: [\n    // Other plugins...\n\n    new ManifestPlugin(), // Important! This must come before the ReactStaticPlugin\n    new ReactStaticPlugin({\n      routes: './client/routes.js',\n      template: './template.js',\n    }),\n  ],\n};\n```\n\n```js\n// template.js\nconst React = require('react');\nconst T = React.PropTypes;\n\nconst Html = ({ title = 'Amazing Default Title', body, manifest }) =\u003e (\n  \u003chtml lang='en'\u003e\n    \u003chead\u003e\n      \u003cmeta charSet='utf-8' /\u003e\n      \u003cmeta httpEquiv='X-UA-Compatible' content='IE=edge' /\u003e\n      \u003cmeta name='viewport' content='width=device-width, initial-scale=1' /\u003e\n      \u003ctitle\u003e{title}\u003c/title\u003e\n      \u003clink rel='stylesheet' href={manifest['app.css']} /\u003e\n    \u003c/head\u003e\n    \u003cbody\u003e\n      \u003cdiv id='root' dangerouslySetInnerHTML={{ __html: body }} /\u003e\n      \u003cscript src={manifest['app.js']} /\u003e\n    \u003c/body\u003e\n  \u003c/html\u003e\n);\n\nHtml.propTypes = {\n  title: T.string,\n  body: T.string,\n  manifest: T.object.isRequired,\n};\n\nmodule.exports = Html;\n```\n\nNOTE: In the above template file the `href` for the stylesheet as well as the `src` for the script tag are specified as keys on the `manifest` object.\n\n[webpack-manifest-plugin]: https://github.com/danethurber/webpack-manifest-plugin\n\n## Roadmap\n\n- [x] Custom HTML layout option\n- [x] Improved testing\n- [x] JSX templating support\n- [x] Redux support\n- [ ] Support for dynamic routes + data (i.e. `\u003cRoute path='post/:id' /\u003e`)\n- [ ] Custom 404 page filename option\n- [ ] Passing all props from `\u003cRoute\u003e` components and React Router to your template component as props (See #12)\n\n## Development\n\nThe source for this plugin is transpiled using Babel. Most importantly this allows us to use JSX, but it also provides access to all ES6 features. During development you probably want to watch the source files and compile them whenever they change. To do this:\n\n#### To `watch`\n\n```\nnpm run watch\n```\n\n#### To `build`\n\n```\nnpm run build\n```\n\nMake sure to run the project locally to be sure everything works as expected (we don't yet have a test suite). To do this link this repo locally using NPM. From the source directory:\n\n```\nnpm link .\n```\n\nThen you can link it within any local NPM project:\n\nNow when you `require` or `import` it you will get the local version.\n\n```\nnpm link react-static-webpack-plugin\n```\n\n#### To `test`\n\nFirst, make sure you've installed all the test dependencies. This means installing all `node_modules` within the `example/` directory. You can do this with the provided script.\n\n```\n./install_test_dependencies.sh\n```\n\nNow you can run the tests:\n\n```\nnpm test\n```\n\nRuns ESLint, Flow type checking and the suite of Wepback tests.\n\n#### Running individual tests\n\nIf there is one specific test failing and you want to run it individually you can do so. Make sure you have `ava` installed globally:\n\n```\nnpm install -g ava\n```\n\nThen you can run invidual tests by running a command similar to this. For example, to test only the Redux tests you can run:\n\n```\nNODE_ENV=production DEBUG=react-static-webpack-plugin* ava --verbose  ./example/redux/test.js\n```\n\nThe `DEBUG` env variable tells the plugin to be very verbose in its logging output.\n\n[boilerplate]: https://github.com/iansinnott/react-static-boilerplate\n\n## License\n\nMIT © [Ian Sinnott](http://iansinnott.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiansinnott%2Freact-static-webpack-plugin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiansinnott%2Freact-static-webpack-plugin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiansinnott%2Freact-static-webpack-plugin/lists"}