All Projects → adrianmcli → next-boilerplate

adrianmcli / next-boilerplate

Licence: MIT license
📐 A modern universal boilerplate for React applications using Next.js.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to next-boilerplate

core
Server side rendering with The Elm Architecture in Deno
Stars: ✭ 16 (+6.67%)
Mutual labels:  jsx
math-magicians
Website for all fans of mathematics. It is a Single Page App (SPA) that allows users to make simple calculations. read a random motivation-related quote and write a to-do list.
Stars: ✭ 22 (+46.67%)
Mutual labels:  jsx
ExtendScript-for-Visual-Studio-Code
Extension that adds Adobe ExtendScript support to Visual Studio Code
Stars: ✭ 29 (+93.33%)
Mutual labels:  jsx
studybuddy-web
📚 Website for all the study materials for IIITV curriculums 🎉
Stars: ✭ 17 (+13.33%)
Mutual labels:  jsx
React-Netflix-Clone
A Fully Responsive clone of Netflix website built using React.JS as a Front-end & Firebase as a Back-end.
Stars: ✭ 91 (+506.67%)
Mutual labels:  jsx
react-calculator
📐 PWA React + Redux Calculator
Stars: ✭ 65 (+333.33%)
Mutual labels:  jsx
string-dom
Create HTML strings using JSX (or functions).
Stars: ✭ 13 (-13.33%)
Mutual labels:  jsx
BarterOnly
An ecommerce platform to buy or exchange items at your convenience
Stars: ✭ 16 (+6.67%)
Mutual labels:  jsx
vue3-jd-h5
🔥 Based on vue3.0.0, vant3.0.0, vue-router v4.0.0-0, vuex^4.0.0-0, vue-cli3, mockjs, imitating Jingdong Taobao, mobile H5 e-commerce platform! 基于vue3.0.0 ,vant3.0.0,vue-router v4.0.0-0, vuex^4.0.0-0,vue-cli3,mockjs,仿京东淘宝的,移动端H5电商平台!
Stars: ✭ 660 (+4300%)
Mutual labels:  jsx
babel-plugin-hyperscript-to-jsx
This plugin transforms react-hyperscript into JSX. Intended to be used as codemod.
Stars: ✭ 20 (+33.33%)
Mutual labels:  jsx
ECHI VUE TODO
使用 Vue 开发的一款 TODO 应用,包含登录、待办、日程、历史事项、回收站。项目较为小型,适合进阶学习使用(💡请注意,项目大量使用 jsx 进行开发)。
Stars: ✭ 19 (+26.67%)
Mutual labels:  jsx
react-ui-components
React UI Components (npm @assenti/rui-components)
Stars: ✭ 21 (+40%)
Mutual labels:  jsx
react-jsx-renderer
A React component for Rendering JSX
Stars: ✭ 43 (+186.67%)
Mutual labels:  jsx
remark-jsx
A simple way to use React inside Markdown.
Stars: ✭ 29 (+93.33%)
Mutual labels:  jsx
nornj
More exciting JS/JSX based on Template Engine, support control flow tags, custom directives, two-way binding, filters and custom operators.
Stars: ✭ 97 (+546.67%)
Mutual labels:  jsx
ios-scriptable-tsx
在 vscode 上使用 typescript 和 jsx 开发 ios 小组件的小框架.基于 Scriptable app.
Stars: ✭ 113 (+653.33%)
Mutual labels:  jsx
crud-app
❄️ A simple and beautiful CRUD application built with React.
Stars: ✭ 61 (+306.67%)
Mutual labels:  jsx
adobe-discord-rpc
Discord Rich Presence extension for your adobe apps!
Stars: ✭ 383 (+2453.33%)
Mutual labels:  jsx
ink-box
Styled box component for Ink
Stars: ✭ 113 (+653.33%)
Mutual labels:  jsx
fjb
fast javascript bundler 📦
Stars: ✭ 103 (+586.67%)
Mutual labels:  jsx

Next Boilerplate 📐

A modern universal boilerplate for React applications using Next.js.

Features 🎉

  • Isolated package-based development
  • Redux + Redux-Observable for state management
  • Server-side rendering (comes out of the box from Next.js)
  • Linting with AirBnB and CleanJS

Warning

This boilerplate is in heavy development. Please keep this in mind as you evaluate this repository and read the following paragraphs.

Table of Contents

Modules 📦

There are currently four modules in this boilerplate example:

  • core — Holds a simple string variable in order to demonstrate importing from a module.
  • redux-config — Configures Redux; It is also where all the reducers and initial app states are combined.
  • counter — Implements a Redux-based increment/decrement counter with its own actions, reducers, and initial state.
  • todoapp — Implements a simple Redux-based todo app similar to the counter module, but with more complex containers and components.
  • fetch — Implements an asynchronous fetching example. It fetches from an API endpoint and displays the resulting data.

Conventions

As 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.

Feature-based modules

A module is just a folder underneath the modules directory.

The most important thing to remember is: Each feature of the app should reside in its own module.

Within 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.

You 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.

Actions and Reducers

Redux actions and reducers should reside in their own feature-based modules. This also includes more advanced objects/functions/files like Sagas (Redux-Saga) and Epics (Redux-Observable).

The 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.

The redux-config module is then configured to pull in the necessary reducers and initial state from each feature module.

Importing

Importing 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.

For example, if there is a counter module with an index.js like this:

import Container from './containers/Counter'
import CounterReducer from './reducer'

export const Counter = Container
export const reducer = CounterReducer

We can import it from our pages directory (or inside any other modules) like this:

import { Counter, reducer } from '~/counter'

Note 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:

import { Counter, reducer } from '../modules/counter'

You should never be using relative imports unless you are only working with the internals of a module.

Exporting

Inside each module, the index.js file will have named exports for anything that the module chooses to expose to the rest of the app.

For 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.

Container-Component model

Inside each React-based module, there should be two folders: components and containers.

components is where all the presentational or "dumb" components will live. These components must be functional stateless components.

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.

If 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.

Number of files

Ideally, there shouldn't be more than five files inside each of the components and containers folders.

If you find that you require more files to implement your feature, try to split out the feature into another module with a namespaced prefix.

For 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:

modules/counter-core/
modules/counter-data/
modules/counter-components/

Example

containers/Counter.js

import { connect } from 'react-redux'
import Component from '../components/Counter'

const mapDispatchToProps = (dispatch) => ({
  increment: () => dispatch({ type: 'INCREMENT' }),
  decrement: () => dispatch({ type: 'DECREMENT' }),
})

export default connect(state => state, mapDispatchToProps)(Component)

components/Counter.js

export default ({ count = 0, increment, decrement }) =>
  <div>
    <h1>{ count }</h1>
    <button onClick={ increment }>Increment</button>
    <button onClick={ decrement }>Decrement</button>
  </div>

Namespaced Modules

When 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.

Taking 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.

For an example of this, please see the my-feature page.

Example

Inside 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:

import { hello } from '~/@my-feature/core'
import { add } from '~/@my-feature/utils'

Note that there is nothing different or special about these modules, they simply sit within another folder.

Asynchronicity with Observables

Redux-Observable is used along with 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.

Each 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.

The fetch page and module is a good demonstration of this architecture.

Example

Here is an example epic.js, referenced from the fetch module:

import { START_REQUEST, responseReceived } from './actions'

const request$ = Observable
  .ajax({ url: 'https://jsonplaceholder.typicode.com/posts/1' })
  .map(data => data.response)

export default action$ =>
  action$.filter(action => action.type === START_REQUEST)
    .exhaustMap(() => request$)
    .map(responseReceived)

All 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$.

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.

Redux Thunks

Redux-Observable may be overkill for certain simple use-cases. As a result, you may choose to use Redux-Thunk, but do try to isolate your asynchronous code in a specific file for ease of reasoning.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].