• Stars
    star
    107
  • Rank 323,587 (Top 7 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 5 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Jam3 NextJS Generator for SPA, SSG, SSR and JAMStack applications

Experience.Monks NextJS Generator

GitHub

NextJS Boilerplate for static and server side rendered projects

https://generator.jam3.net

Icon made by Pixel perfect from www.flaticon.com


Table of Contents

 

Installation

Check your Node and NPM versions

Make sure you are using Node 16.15.1 and NPM 8.11.0 on your development environment to match CircleCI setup. Using NVM is highly encouraged.

$ nvm install 16.15.1
$ nvm use 16.15.1

TIP: You can deeply integrate into your shell to automatically invoke NVM when changing directories.: https://github.com/nvm-sh/nvm#deeper-shell-integration

 

Install git-lfs

Download git-lfs by following the steps based on your operating system.

  • Debian / Ubuntu

    $ curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | sudo bash
    $ sudo apt-get install git-lfs
  • MacOS (Using Homebrew)

    $ brew update
    $ brew install git-lfs
  • Windows
    Download and run the latest windows installer from https://github.com/git-lfs/git-lfs/releases.

 

Set up your linters

In order to save time developing it is highly recommended to have proper linters set up. Below are instructions for VSCode (if you use other editor configure it in a similar way).

 

Clone the GitHub repository

$ git clone [email protected]:Experience-Monks/nextjs-boilerplate.git

 

Install NPM dependencies:

$ npm install

 

Usage

Local Front End server

$ npm run dev

The command above will start the development server on several different ports:

Template scripts

We are using seng-generator to generate templates

# create a simple component
$ npm run generate component [component-name]

# create a page component (the generated files will be automatically prefixed by "Page")
$ npm run generate page [page-name]

# create a new route (use the same name used for generating the page component)
$ npm run generate route [page-name]

 

File Structure And Organization

This is the base folder structure on this project:

public/
src/
    assets/
    components/
    data/
    hooks/
    pages/
    services/
    styles/
    svgs/
    utils/

In order to make our life easier we have an import alias set up.
So instead of using relative imports, you can reference the .src/ folder with an @/.
Example:

import log from '@/utils/log'

This will import the log util from ./src/utils/log doesn't matter from where you call it.

It also works inside SCSS files:

src: url('~@/assets/fonts/ShopifySans/ShopifySans-Regular.woff2');

 

Here is the folder breakdown:

 

public/

public/
    favicons/
    images/
    ...

This is our static files folder.
All items in this folder will be copied to the output directory at build time.
Put here items that need to be publicly accessible and not hashed, like favicons, robots.txt, sitemap, share images, etc.
If you need to reference them in your code, just use a simple url, like:

<link rel="shortcut icon" href="/common/favicons/favicon.ico" />

WARNING: Never import / require files from this folder. This would result in duplicated files in the output directory.

 

src/assets/

src/
    assets/
        fonts/
        images/
        videos/
        ...

Here we have all the binary assets to be imported / required in our code. Everything inside this folder will be compiled, renamed (hashed), and placed in /_next/static/ folder at build time. Add here binary files only.

 

src/components/

src/
    components/
        Component/
            MyComponent.module.scss
            MyComponent.stories.js
            MyComponent.tsx
        ...

”The best way to write big applications is to never write big applications”.

This saying resonates strongly with React.
Let’s make sure all our components are independent and isolated, in other words, as pure as possible.
Treat components as individual applications used to compose the final app.
They should be able to be imported everywhere and should be developed with reusability in mind.

We know sometimes it is hard to keep everything pure. With this in mind our generator produces components that are split into View and Controller components. If you have to, implement any global dependency on the Controller component, leaving the View component pure and testable:

  • View: A pure and testable component, receives props from the controller
  • Controller: Handles global state, router, data fetching, etc. Feed and forward props to the view component.
// MyComponent.tsx

export interface MyComponentProps {
  // List here all props that are public and settable by the parent component.
  className?: string
}

export interface ViewProps extends MyComponentProps {
  // List here the private props that are only settable by the controller component.
  loggedIn: boolean
}

// View (pure and testable component, receives props from the controller)
export const View: FC<ViewProps> = ({ className, loggedIn }) => {
  return (
    <div className={classNames('MyComponent', css.root, className)}>
      {loggedIn ? 'You are logged in' : 'You are logged out'}
    </div>
  )
}

// Controller (handles global state, router, data fetching, etc. Feeds props to the view component)
const MyComponent: FC<MyComponentProps> = (props) => {
  const [loggedIn] = localStore((state) => [state.loggedIn], shallow)
  return <View {...props} loggedIn={loggedIn} />
}

export default memo(MyComponent)

Let's follow some practices in order to make our life easier:

  • Keep components as decoupled as possible.

    • It means to avoid global scoped dependencies like Router, Redux, Zustand, or any global state wiring in the View components, use the Controller component for that.

    • In the View components everything should come in as props, and out as callbacks. Consider each component as an isolated application.

    With this structure you will be able to create Storybook stories and test the View component with ease:

    // MyComponent.stories.tsx
    
    import { View, ViewProps } from './MyComponent'
    
    export default { title: 'components/MyComponent' }
    
    export const LoggedIn: Story<ViewProps> = (args) => <View {...args} />
    LoggedIn.args = { loggedIn: true }
    
    export const LoggedOut: Story<ViewProps> = (args) => <View {...args} />
    LoggedOut.args = { loggedIn: false }

 

  • Avoid components namespacing.

    Let’s keep all of our components in the same folder (src/components) and avoid creating subfolders like src/components/buttons/Accept, src/components/modals/Accept, etc. This will give us visibility of all components and prevent components with the same name to be created inadvertently.
    Instead, let’s group similar components by prefixing them, so they appear grouped in the src/components.
    Examples: ButtonAccept, ButtonRound, ButtonXYZ, Carousel, CarouselItem, ModalAccept, etc…

 

  • Common style structure.

    Let’s make sure to always implement our component styling in a predictable way:

    // MyComponent.tsx
    
    import css from '#/css/components/MyComponent/MyComponent.module.css'
    
    ...
    export const View: FC<ViewProps> = ({ className, ... }) => {
      return (
        <div className={classNames('MyComponent', css.root, className)}>
          ...
        </div>
      );
    };
    ...

    And in our MyComponent.module.scss files:

    // MyComponent.module.scss
    
    @import 'shared';
    
    .root {
      ...
    }

    This pattern give us 2 immediate advantages:

    • By having a string class name with the same name of the component ('MyComponent'), we are able to query children components with ease. This is specially useful when we start to implement animations. This hardcoded string class name should be only used for DOM selection purposes, though, never for styling.
    • By having the CSS module css.root class, we create a good standard by signaling the top-most class in every component. This will also compile nicely to something like MyComponent__root__xyzw, making our life easier when we have to debug.

 

  • Stories are important.

    Let’s make sure all components have Storybook stories with proper controls set up.
    It is not only important for testing but also works as documentation for other developers.
    This way we can easily check how components works and if they fit our needs in a particular scenario, preventing us from creating several similar components unnecessarily, and even more important, allowing us to make changes to any specific component directly without having to, for instance, launch the entire project, navigate to a specific page, add some flags or comment chunks of code, and so on just for checking how a single component works. In the end, having good stories speeds up the development (specially towards the QA/end phases of a project).

 

src/data/

src/
    data/
        content.json
        config.json
        types.ts
        ...

Here we have our configuration and string content.
These files will be consumed at build time to populate our pages.
Never import content.json directly inside components as it might make it harder to switch to a CMS in the future. Page components should receive the required strings through Next.js getStaticProps() method.

See Copy Management section below for more details.

 

src/hooks/

src/
    hooks/
        use-layout.ts
        use-window-size.ts
        ...

Add here any custom React hook you might need. We have several useful hooks already implemented, check them out on.

 

src/pages/

src/
    pages/
        _app.tsx
        _document.tsx
        index.tsx
        ...

Here we have all the Next.js routing logic.
It was separated from the React components logic on purpose. Here are the main reasons:

  • A better separation of concerns. Next.js logic will most times be executed in the NodeJS context, not the browser's. And this very often leads to confusion.
  • Prevents route pollution. Next.js by default looks for every file inside this folder to generate the routing logic. By separating it from the components we are now able to have proper naming convention for our pages, and have storybook stories of our pages without the risk of generating unwanted routes.
  • Flexibility. Depending on our needs we might have to, for instance, render different components for the same route (issue often found phased projects). By having the Next routing logic separated from the React components logic we can easily achieve this and much more.

 

src/services/

src/
    services/
        raf.ts
        resize.ts
        ...

If you ever worked with Angular this will be strightforward to you.
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
In this project, services are singleton class instances that abstract, and prevent, the need of adding several event listeners for the same browser events, like resize, requestAnimationFrame, etc. Consider using services for events that are prone to be listened by many components.

 

src/styles/

src/
    styles/
        global.scss
        mixins.scss
        ...

Global SASS stuff.

 

src/svgs/

src/
    svgs/
        ArrowLeft.svg
        Logo.svg
        ...

Since SVGs are not binary assets, they are code that generate GIT diffs and can be used in several scenarios other than just rendering some graphics on the screen. They are like another class of application components, like hooks and services are.
In the end of the day they are most often compiled through SVGR to be imported and used like regular components, but, are not able to perform imports and don't have any associated style file.
Also, they don't follow same file and code structure as components, and they don't require individual folders.
For these reasons they have this special folder instead of being placed inside assets/ or components/ folders.
Storybook-wise, we have a special file (.storybook/intro/Svg.stories.tsx) that automaticaly creates a catalog of all SVGs placed here.

 

src/utils/

src/
    utils/

Our beloved utility belt.

 

Copy Management

A single source of truth: ./src/data/content.json

A very good practice is to have all content grouped in a single source of truth. This allow us to change them quickly and prevents wasting time hunting individual copies through components when we need to make adjustments.
Also, it allows us to send the entire json file for translation or corrections when required, making our life easier and more organized in the end of the day. Switching to a CMS in the future will also be much easier, since we will most likely just need to change the data source.

Copy injection flow

That is why it is important to never import content.json inside our components. Instead, import it in your route files (./src/pages/*) and inject only the required content through the getStaticProps() method.

For instance, considering this content on content.json:

{
  "common": {
    "play": "play",
    "pause": "pause",
    "close": "close",
    "screenRotate": {
      "title": "Please rotate\nyour device"
    }
  },
  "pageLanding": {
    "head": {...}
    },
    "body": {
      "title": "Landing Page"
    }
  }
}

We could inject the copy this way inside our route file:

import { GetStaticProps } from 'next'

import strings from '@/data/content.json'

import PageLanding, { PageLandingProps } from '@/components/PageLanding/PageLanding'

export const getStaticProps: GetStaticProps<PageLandingProps> = async () => {
  return {
    props: {
      head: strings.pageLanding.head,
      common: strings.common,
      strings: strings.pageLanding.body
    }
  }
}

export default PageLanding

Note the PageLanding component will receive only the pageLanding content as props. It will then forward the received content to child components. This allow us to have a single entry point for all the copy that can be easily changed in the future to a CMS or any other data source.

  graph TD;
  data/content.json-->pages/index.tsx;
      pages/index.tsx-->components/Layout;
      components/Layout-->components/PageLanding;
      components/Layout-->components/ScreenRotate;
      components/Layout-->components/ScreenNoScript;
      components/PageLanding-->components/A;
      components/PageLanding-->components/B;
      components/B-->components/C;
      components/B-->components/D;
      components/ScreenRotate-->..;
      components/ScreenNoScript-->...;

 

Powerful copy with less typing!

We have a very useful util for handling copy at @/utils/copy.ts. It facilitates content handling, performs xss cleanups and allows us to interpolate values on every string. For example, lets consider we have this copy in our content.json:

{
  "hello": "Hi! {name}!\nHow are you?\nFancy for a {meal}?"
}

We could implement it inside our components like:

<h1 {...copy.html(content.hello, {name: 'John Doe', meal: 'drink'})} />

It would result in something like:

<h1 dangerouslySetInnerHTML={{ __html: 'Hello John Doe!<br />How are you?<br />Fancy for a drink?' }} />

Check the ./src/utils/copy.ts implementation for more details.

 

License

MIT

More Repositories

1

math-as-code

a cheat-sheet for mathematical notation in code form
14,818
star
2

devtool

[OBSOLETE] runs Node.js programs through Chromium DevTools
JavaScript
3,774
star
3

nice-color-palettes

nice colour palettes as JSON
JavaScript
848
star
4

three-bmfont-text

renders BMFont files in ThreeJS with word-wrapping
JavaScript
764
star
5

glsl-fast-gaussian-blur

optimized single-pass blur shaders for GLSL
JavaScript
659
star
6

hihat

🎩 local Node/Browser development with Chrome DevTools
JavaScript
447
star
7

jam3-lesson-webgl-shader-threejs

Using custom vertex and fragment shaders in ThreeJS
JavaScript
361
star
8

jam3-lesson-webgl-shader-intro

A brief introduction to fragment shaders.
307
star
9

web-audio-player

a cross-browser WebAudio player
JavaScript
244
star
10

360-image-viewer

A standalone panorama viewer with WebGL
JavaScript
243
star
11

ae-to-json

will export an After Effects project as a JSON object
JavaScript
225
star
12

msdf-bmfont

Generate BMFont texture and spec using msdfgen
JavaScript
161
star
13

jam3-lesson

142
star
14

audiobuffer-to-wav

convert an AudioBuffer to .wav format
JavaScript
132
star
15

Invisible-Highway

Invisible Highway is an experiment in controlling physical things in the real world by drawing in AR. Simply make a pathway along the floor on your phone and the robot car will follow that path on the actual floor in your room. A custom highway with scenery is generated along the path to make the robots a little more scenic on your phone screen.
C#
130
star
16

awesome-streetview

beautiful [lat, lng] Google Street View locations
JavaScript
129
star
17

ffmpeg-gif

shell script to convert video to high quality GIF with ffmpeg
JavaScript
124
star
18

jam3-lesson-module-basics

intro to modular programming for frontend JavaScript
113
star
19

orbit-controls

generic controls for orbiting a target in 3D
JavaScript
110
star
20

svg-to-image

convert SVG text to a Image that can be drawn in canvas
JavaScript
103
star
21

voice-activity-detection

Voice activity detection
JavaScript
102
star
22

extract-streetview

extract street view spherical images and depth information
JavaScript
101
star
23

react-f1

F1 ui animation library for React
JavaScript
90
star
24

webgl-react-boilerplate

WebGL React App ⚡️
JavaScript
87
star
25

opentype-layout

word wraps and lays out Opentype.js glyphs
JavaScript
86
star
26

chaikin-smooth

Chaikin's smoothing algorithm for 2D polylines
JavaScript
82
star
27

f1

A stateful ui library
JavaScript
78
star
28

touch-scroll-physics

scroll physics for a scroll pane with edge bounce and velocity
JavaScript
77
star
29

preloader

A library for loading common web assets
JavaScript
69
star
30

three-png-stream

streams ThreeJS render target pixel data
JavaScript
66
star
31

layout-bmfont-text

word-wraps and lays out text glyphs
JavaScript
59
star
32

react-background-video-player

React background video component with simple player API
JavaScript
58
star
33

ios-safe-audio-context

create a WebAudio context that works in iOS and everywhere else
JavaScript
57
star
34

perspective-camera

a high-level 3D perspective camera
JavaScript
53
star
35

glsl-hsl2rgb

HSL to RGB color conversion in GLSL
GLSL
50
star
36

touches

simplified touch/mouse events for flick and swipe
JavaScript
45
star
37

ios-video-test

a test of inline iOS video playback
JavaScript
45
star
38

three-path-geometry

thick 2D lines for ThreeJS
JavaScript
42
star
39

jam3-lesson-canvas2d

open source code for a Canvas2D workshop at Jam3
JavaScript
41
star
40

maya-json-export

a generic Maya to JSON exporter for triangle meshes
JavaScript
41
star
41

glsl-100-to-300

transpiles GLSL tokens from v100 to v300 es
JavaScript
39
star
42

xhr-request

tiny http client for Node and the browser
JavaScript
39
star
43

webvr-gearvr-test

a simple test of WebVR running natively in GearVR
JavaScript
38
star
44

generator-jam3

This is a generator for Jam3 projects
JavaScript
37
star
45

google-maps-api

Get up and running with the google maps API quickly
JavaScript
34
star
46

babel-plugin-static-fs

statically transforms Node fs for the browser
JavaScript
33
star
47

ae-to-json-cli

This is a command line application to export After Effects files as JSON
JavaScript
33
star
48

tech-we-use

A list of technologies: modules, libraries, and tools we use
32
star
49

load-bmfont

loads a BMFont file in Node and the browser
JavaScript
31
star
50

jam3-testing-tools

a brief intro to testing tools
30
star
51

gl-pixel-stream

streaming gl.readPixels from an FBO
JavaScript
30
star
52

jam3-lesson-module-creation

introduction to creating a new npm module
29
star
53

threejs-generate-gif

Generate an animated GIF (using the GPU) from a threejs scene
JavaScript
29
star
54

tap-dev-tool

prettifies TAP in the browser's console
JavaScript
29
star
55

text-split

Utility for splitting text into chunks based on regex.
JavaScript
27
star
56

three-simplicial-complex

render simplicial complexes with ThreeJS
JavaScript
27
star
57

three-buffer-vertex-data

an easy way to set vertex data on a BufferGeometry
JavaScript
27
star
58

parse-dds

parses the headers of a DDS texture file
JavaScript
26
star
59

threejs-post-process-example

a tutorial on ThreeJS post processing
JavaScript
24
star
60

add-px-to-style

Will add px to the end of style values which are Numbers
JavaScript
24
star
61

google-panorama-by-location

gets a Google StreetView by [ lat, lng ]
JavaScript
24
star
62

mesh-heightmap-contours

Given a heightmap, generate a "contoured" terrain mesh
JavaScript
24
star
63

detect-import-require

list require and import paths from a JavaScript source
JavaScript
24
star
64

says

cross-platform 'say' command using Electron
JavaScript
23
star
65

background-cover

Simulate 'background-size: cover' on HTMLVideoElement and HTMLImageElement
JavaScript
23
star
66

analyser-frequency-average

gets an average intensity between two frequency ranges
JavaScript
23
star
67

webgl-components

Modular components and utilities used for WebGL based projects 💅
TypeScript
22
star
68

video-element

A simple HTML5/YouTube Video Element with a unified interface
JavaScript
22
star
69

three-fluid-demo

ThreeJS fluid simulation demo
GLSL
22
star
70

delaunify

randomly delaunay-triangulates an image
JavaScript
22
star
71

camera-unproject

unproject 2D point to 3D coordinate
JavaScript
21
star
72

heightmap-contours

Generate a series of 2D contour meshes over a heightmap
JavaScript
21
star
73

glsl-blend-overlay

blend mode 'overlay' for GLSL
C
20
star
74

ray-3d

a high-level ray picking helper for 3D intersection
JavaScript
19
star
75

scroll-manager

A handler for scrolling inside elements with different eases
JavaScript
19
star
76

touch-pinch

minimal two-finger pinch gesture detection
JavaScript
19
star
77

three-orbit-viewer

quick harness for viewing a mesh with orbit viewer
JavaScript
18
star
78

css-transform-to-mat4

Will take a string which is a css transform value (2d or 3d) and return a mat4 or 3d transformation matrix from the string.
JavaScript
17
star
79

gl-shader-output

test a shader's gl_FragColor output on a 1x1 canvas
JavaScript
17
star
80

gh-api-stream

streams JSON content from a GitHub API
JavaScript
17
star
81

uploadr

CLI tool which uploads a folder via SFTP
JavaScript
17
star
82

camera-spin

Mouse/touch-draggable first-person camera
JavaScript
17
star
83

exif-orientation-image

Properly displays an image via canvas based on the exif orientation data.
JavaScript
16
star
84

jam3-lessons-react

Quick and brief reference for React development
15
star
85

preview-dds

preview and save DDS textures from the command line
JavaScript
15
star
86

meetup-creative-coding-webgl

A WebGL experiment created for Jam3's Creative Coding Meetup on October 23rd 2019.
JavaScript
14
star
87

detect-audio-autoplay

detects whether the browser can auto-play audio
JavaScript
14
star
88

ae-threejs-multichannel-sdf

A signed distance field Effect plugin for Adobe After Effects
C
13
star
89

slot-machine-button

🎰 React Slot Machine Button
JavaScript
13
star
90

google-maps-image-api-url

This module will return a string which is a url to load an image from the Google Maps Image API
JavaScript
13
star
91

webgl-to-canvas2d

Convert a webgl context or webgl canvas into a 2d canvas
JavaScript
13
star
92

f1-dom

Create f1 ui with the dom
JavaScript
12
star
93

nyg

Not another yeoman generator, a simplified project generator based around prompts and events.
JavaScript
12
star
94

camera-picking-ray

creates a picking ray for a 2D/3D camera
JavaScript
11
star
95

scroll-bar-width

Detect browser scrollbar size
JavaScript
11
star
96

nyg-jam3

Second generation of the Jam3 Generator, many new features and breaking change features
JavaScript
11
star
97

awwwards-stream

scrape Awwwards data
JavaScript
11
star
98

adviser

Jam3 quality advisor. Integrates checking for best practices at Jam3
JavaScript
11
star
99

google-assistant-21-days-of-gratitude

JavaScript
11
star
100

google-assistant-greeting-cards

JavaScript
10
star