{"id":16597259,"url":"https://github.com/adrianmcli/next-boilerplate","last_synced_at":"2025-09-05T01:45:16.736Z","repository":{"id":73736845,"uuid":"81790217","full_name":"adrianmcli/next-boilerplate","owner":"adrianmcli","description":"📐 A modern universal boilerplate for React applications using Next.js.","archived":false,"fork":false,"pushed_at":"2017-12-21T07:58:15.000Z","size":178,"stargazers_count":14,"open_issues_count":2,"forks_count":16,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-09-05T01:45:08.495Z","etag":null,"topics":["boilerplate","javascript","jsx","nextjs","react","redux"],"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/adrianmcli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2017-02-13T05:57:54.000Z","updated_at":"2023-06-10T06:09:09.000Z","dependencies_parsed_at":"2023-04-01T10:32:54.082Z","dependency_job_id":null,"html_url":"https://github.com/adrianmcli/next-boilerplate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/adrianmcli/next-boilerplate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fnext-boilerplate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fnext-boilerplate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fnext-boilerplate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fnext-boilerplate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adrianmcli","download_url":"https://codeload.github.com/adrianmcli/next-boilerplate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adrianmcli%2Fnext-boilerplate/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273699722,"owners_count":25152285,"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","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","javascript","jsx","nextjs","react","redux"],"created_at":"2024-10-11T23:55:21.573Z","updated_at":"2025-09-05T01:45:11.720Z","avatar_url":"https://github.com/adrianmcli.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Next Boilerplate 📐\nA modern universal boilerplate for React applications using [Next.js](https://github.com/zeit/next.js).\n\n### Features 🎉\n\n* Isolated package-based development\n* Redux + Redux-Observable for state management\n* Server-side rendering (comes out of the box from Next.js)\n* Linting with [AirBnB](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb) and [CleanJS](https://github.com/bodil/eslint-config-cleanjs)\n\n### Warning\n\nThis boilerplate is in heavy development. Please keep this in mind as you evaluate this repository and read the following paragraphs.\n\n### Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\u003c!-- https://github.com/thlorenz/doctoc --\u003e\n\n- [Modules](#modules-)\n- [Conventions](#conventions)\n  - [Feature-based modules](#feature-based-modules)\n    - [Actions and Reducers](#actions-and-reducers)\n    - [Importing](#importing)\n    - [Exporting](#exporting)\n  - [Container-Component model](#container-component-model)\n    - [Number of files](#number-of-files)\n    - [Example](#example)\n      - [`containers/Counter.js`](#containerscounterjs)\n      - [`components/Counter.js`](#componentscounterjs)\n  - [Namespaced Modules](#namespaced-modules)\n    - [Example](#example-1)\n- [Asynchronicity with Observables](#asynchronicity-with-observables)\n    - [Example](#example-2)\n    - [Redux Thunks](#redux-thunks)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n# Modules 📦\n\nThere are currently four modules in this boilerplate example:\n\n* `core` — Holds a simple string variable in order to demonstrate importing from a module.\n* `redux-config` — Configures Redux; It is also where all the reducers and initial app states are combined.\n* `counter` — Implements a Redux-based increment/decrement counter with its own actions, reducers, and initial state.\n* `todoapp` — Implements a simple Redux-based todo app similar to the `counter` module, but with more complex containers and components.\n* `fetch` — Implements an asynchronous fetching example. It fetches from an API endpoint and displays the resulting data.\n\n# Conventions\n\nAs per Next.js convention, everything starts at the `pages` directory. Beyond this (and other default conventions), we have decided to add some additional conventions to improve the developer experience.\n\n## Feature-based modules\n\nA module is just a folder underneath the `modules` directory.\n\nThe most important thing to remember is: **Each feature of the app should reside in its own module.**\n\nWithin each of these modules, there should only be a **single point of entry**: the `index.js` file. This is where the module will expose its contents to the rest of the app.\n\nYou should only import from `index.js` and not any of the module's internal files. The idea is to maximize reusability and remain flexible to future changes by having a single file that holds the interface to the entire module.\n\n### Actions and Reducers\n\nRedux actions and reducers should reside in their own feature-based modules. This also includes more advanced objects/functions/files like Sagas ([Redux-Saga](https://github.com/redux-saga/redux-saga)) and Epics ([Redux-Observable](https://github.com/redux-observable/redux-observable)).\n\nThe idea is to ensure that **each module fully contains everything required for the implementation of its feature**. That way, when you need to go change something, you'll know that everything you need will be inside that module.\n\nThe `redux-config` module is then configured to pull in the necessary reducers and initial state from each feature module.\n\n### Importing\n\nImporting from a feature-based module is simple. We use a Babel plugin (`babel-plugin-root-import`) so that we can minimize our usage of relative paths.\n\nFor example, if there is a `counter` module with an `index.js` like this:\n\n```js\nimport Container from './containers/Counter'\nimport CounterReducer from './reducer'\n\nexport const Counter = Container\nexport const reducer = CounterReducer\n```\n\nWe can import it from our `pages` directory (or inside any other modules) like this:\n\n```js\nimport { Counter, reducer } from '~/counter'\n```\n\nNote that we use the `~` symbol to mark `counter` as an internal app module. The Babel transform will convert the above line to something like this:\n\n```js\nimport { Counter, reducer } from '../modules/counter'\n```\n\nYou should never be using relative imports unless you are only working with the internals of a module.\n\n### Exporting\n\nInside each module, the `index.js` file will have **named exports** for anything that the module chooses to expose to the rest of the app.\n\nFor all other files inside the module, they should all be **default exports** so that each file's responsibility is made clear by what it is exporting. Note that this is not a hard rule, as Redux actions can be considered an exception to this rule. The reasoning for this exception is due to the fact that actions usually are not large enough to warrant their own file. As a result, we group them all into one `actions.js` file and use named exports to expose them.\n\n## Container-Component model\n\nInside each React-based module, there should be two folders: `components` and `containers`.\n\n`components` is where all the presentational or \"dumb\" components will live. These components must be **functional stateless components**.\n\n`containers` is where all the \"smart\" components will live. These are components that may contain state, and also will be tasked with `connect()`-ing to the redux store so that we can provide the appropriate props and actions to a consuming component.\n\nIf these containers become too complicated, split the file into two containers instead, with one in charge of connecting to the redux store, and the other to handle local component state.\n\n### Number of files\n\nIdeally, there shouldn't be more than five files inside each of the `components` and `containers` folders.\n\nIf you find that you require more files to implement your feature, try to split out the feature into another module with a namespaced prefix.\n\nFor example, if your `counter` module gets complicated enough to require data-fetching and more fancy presentational components, consider splitting your one `counter` module into several modules like this:\n\n```\nmodules/counter-core/\nmodules/counter-data/\nmodules/counter-components/\n```\n\n### Example\n\n#### `containers/Counter.js`\n\n```js\nimport { connect } from 'react-redux'\nimport Component from '../components/Counter'\n\nconst mapDispatchToProps = (dispatch) =\u003e ({\n  increment: () =\u003e dispatch({ type: 'INCREMENT' }),\n  decrement: () =\u003e dispatch({ type: 'DECREMENT' }),\n})\n\nexport default connect(state =\u003e state, mapDispatchToProps)(Component)\n```\n\n#### `components/Counter.js`\n\n```js\nexport default ({ count = 0, increment, decrement }) =\u003e\n  \u003cdiv\u003e\n    \u003ch1\u003e{ count }\u003c/h1\u003e\n    \u003cbutton onClick={ increment }\u003eIncrement\u003c/button\u003e\n    \u003cbutton onClick={ decrement }\u003eDecrement\u003c/button\u003e\n  \u003c/div\u003e\n```\n\n## Namespaced Modules\n\nWhen your app grows larger and more complex, you might end up having a long list of modules. This is not ideal, and it's probably a good time to refactor some of these modules into their own namespaced modules.\n\nTaking inspiration from NPM, the convention we have decided to use is the `@` symbol. In short, this is basically nested modules, but with only one level of depth allowed and it must be explicitly stated by using the `@` symbol.\n\nFor an example of this, please see the `my-feature` page.\n\n### Example\n\nInside your `modules` folder, create a namespace by creating a directory named `@my-feature`, where `my-feature` is the name of your namespace. Inside this directory, you can place your namespaced modules. Importing from these modules should look something like this:\n\n```js\nimport { hello } from '~/@my-feature/core'\nimport { add } from '~/@my-feature/utils'\n```\n\nNote that there is nothing different or special about these modules, they simply sit within another folder.\n\n# Asynchronicity with Observables\n\n[Redux-Observable](https://github.com/redux-observable/redux-observable/) is used along with [RxJS](http://reactivex.io/rxjs/) for handling asynchronicity in the app. This means that asynchronous activity is handled as streams inside files called \"epics\". Please note that epics are run *after* actions have been handled by the reducers.\n\nEach module that requires asynchronous support will require an epic file. These epic files are then combined inside the `redux-config` module, much like the case for redux reducers.\n\nThe `fetch` page and module is a good demonstration of this architecture.\n\n### Example\n\nHere is an example `epic.js`, referenced from the `fetch` module:\n\n```js\nimport { START_REQUEST, responseReceived } from './actions'\n\nconst request$ = Observable\n  .ajax({ url: 'https://jsonplaceholder.typicode.com/posts/1' })\n  .map(data =\u003e data.response)\n\nexport default action$ =\u003e\n  action$.filter(action =\u003e action.type === START_REQUEST)\n    .exhaustMap(() =\u003e request$)\n    .map(responseReceived)\n```\n\nAll actions come through the `action$` stream after they have already passed through the redux reducer. We begin by filtering for the action we are specifically concerned with. Once we detect that action, we fire off our AJAX request via a nested observable called `request$`.\n\n`request$` is then merged back onto the main stream by `exhaustMap`, and its response is sent through to the `responseReceived` function, thereby dispatching a `RESPONSE_RECEIVED` action with the data payload. Note that you can also use `mergeMap` or `switchMap` depending on your desired behaviour.\n\n### Redux Thunks\n\nRedux-Observable may be overkill for certain simple use-cases. As a result, you may choose to use [Redux-Thunk](https://github.com/gaearon/redux-thunk), but do try to isolate your asynchronous code in a specific file for ease of reasoning.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrianmcli%2Fnext-boilerplate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadrianmcli%2Fnext-boilerplate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadrianmcli%2Fnext-boilerplate/lists"}