🚀 Boilerplate and Starter for Next.js, Tailwind CSS, StoryBook and TypeScript ⚡️ Made with: Next.js, TailwindCSS, SASS/SCSS, PostCSS, Husky, Lint-Staged, VSCode, StoryBook, AWS Amplify and TypeScript.
Clone this project and use it to create your own Next.js project.
Note: There are some improvements that have to do about deployment. Due to this, this boilerplate is not completed yet.
- 🔥 Next.js 12 for Static Site Generator
- 🎨 Integrate with Tailwind CSS and SASS/SCSS
- 💅 PostCSS for processing Tailwind CSS
- 🎉 Type checking TypeScript
- ✅ Strict Mode for TypeScript and React 18
- ✏️ Linter with ESLint (default NextJS, NextJS Core Web Vitals, Tailwind CSS and StoryBook configuration)
- 🛠 Code Formatter with Prettier
- 🦊 Husky for Git Hooks
- 🚫 Lint-Staged for running linters on Git staged files
- 🗂 VSCode configuration: Debug, Settings, Tasks and extension for PostCSS, ESLint, Prettier, TypeScript
- 🙌 StoryBook for streamlining UI development, testing, and documentation
- 🦾 AWS Amplify for providing serverless API easily and faster via GraphQL.
- Node.js 14+ and yarn or npm
In this section, I'm going to share informations via questions about tech stack of this repo using some useful resources.
AWS Amplify helps front-end web and mobile developers build apps faster. It consists of a development framework and a web hosting service to build and deploy secure and scalable mobile and web applications powered by AWS.
AWS Amplify includes a wide variety of open-source libraries and drag-and-drop UI components developers can use as building blocks for their apps. It also has a built-in CLI you can use to build your backend. And for an additional fee, AWS Amplify Console provides web hosting services for your app.
AWS Amplify has components for:
- Data storage
- Keep your app data synced with the cloud, manage your distributed data, and handle subscriptions and messaging.
- Analytics
- Track user sessions and report on their behavior. Set up custom attributes and analyze conversion funnels.
- Push notifications
- Easily manage your campaigns and send messages to your users across multiple channels, including text, email, and push.
- Authentication
- Access ready-to-use workflows for MFA, single sign-on, forgot password, etc.
The framework supports iOS, Android, Web, and React Native mobile apps, and React, Ionic, Angular, and Vue.js for web apps.
AWS Amplify is awesome for people who don’t want to build their backend or other components from scratch. The Amplify components make it easy to set up the building blocks of your app without doing all the legwork yourself.
That’s a pretty broad use case, but that’s the point—it’s meant to appeal to a large base.
The public customer list for AWS Amplify isn’t very robust yet. They do have a couple heavy-hitters though, like millennial-favorite fitness app Noom and location tracking cloud service HyperTrack.
There are a lot of other reviews of the service on the web, too: note this one that says using Amplify “feels like cheating.”
The app that you built with AWS Amplify is also a serverless app that can use some services such as AWS AppSync(for GraphQL interface), AWS Cognito User Pools(for auth), AWS Lambda Functions, AWS DynamoDB etc.
- SASS stands for Syntactically Awesome Stylesheet
- SASS is an extension to CSS
- SASS is a CSS pre-processor
- SASS is completely compatible with all versions of CSS
- SASS reduces repetition of CSS and therefore saves time
- SASS was designed by Hampton Catlin and developed by Natalie Weizenbaum in 2006
- SASS is free to download and use
Stylesheets are getting larger, more complex, and harder to maintain. This is where a CSS pre-processor can help.
SASS lets you use features that do not exist in CSS, like variables, nested rules, mixins, imports, inheritance, built-in functions, and other stuff.
SASS (Syntactically Awesome Style Sheets) is a pre-processor scripting language that will be compiled or interpreted into CSS. SassScript is itself a scripting language whereas SCSS is the main syntax for the SASS which builds on top of the existing CSS syntax. It makes use of semicolons and brackets like CSS (Cascaded Style Sheets).
SASS and SCSS can import each other. Sass actually makes CSS more powerful with math and variable support.
Let’s list down the main difference between SASS and SCSS.
- SASS is used when we need an original syntax, code syntax is not required for SCSS.
- SASS follows strict indentation, SCSS has no strict indentation.
- SASS has a loose syntax with white space and no semicolons, the SCSS resembles more to CSS style and use of semicolons and braces are mandatory.
- SASS file extension is .sass and SCSS file extension is .scss.
- SASS has more developer community and support than SCSS.
Tailwind CSS is basically a utility-first CSS framework for rapidly building custom user interfaces. It is a highly customizable, low-level CSS framework that gives you all of the building blocks you need to build bespoke designs without any annoying opinionated styles you have to fight to override.
The beauty of this thing called tailwind is it doesn’t impose design specification or how your site should look like, you simply bring tiny components together to construct a user interface that is unique. What Tailwind simply does is take a ‘raw’ CSS file, processes this CSS file over a configuration file, and produces an output.
- Faster UI building process
- It is a utility-first CSS framework which means we can use utility classes to build custom designs without writing CSS as in traditional approach.
- No more silly names for CSS classes and Id’s.
- Minimum lines of Code in CSS file.
- We can customize the designs to make the components.
- Makes the website responsive.
- Makes the changes in the desired manner.
- CSS is global in nature and if make changes in the file the property is changed in all the HTML files linked to it. But with the help of Tailwind CSS we can use utility classes and make local changes.
According to the repo, CSS modules are:
CSS files in which all class names and animation names are scoped locally by default.
So CSS Modules is not an official spec or an implementation in the browser but rather a process in a build step (with the help of Webpack or Browserify) that changes class names and selectors to be scoped (i.e. kinda like namespaced).
CSS Modules takes a different approach. Instead of writing plain HTML, we need to write all of our markup in a JavaScript file, like index.js. Here’s an example of how that might work (we’ll take a look at a more realistic example later):
import styles from './styles.css'
element.innerHTML = `<h1 class="${styles.title}">
An example heading
</h1>`
During our build step, the compiler would search through that
styles.css
file that we’ve imported, then look through the JavaScript we’ve written and make the .title class accessible via styles.title. Our build step would then process both these things into new, separate HTML and CSS files, with a new string of characters replacing both the HTML class and the CSS selector class.
Our generated HTML might look like this:
<h1 class="_styles__title_309571057"> An example heading </h1>
Our generated CSS might look like this:
._styles__title_309571057 {
background-color: red;
}
The class attribute and selector
.title
is entirely gone, replaced by this entirely new string; Our original CSS is not being served to the browser at all.
[the classes] are dynamically generated, unique, and mapped to the correct styles.
This is what is meant by styles being scoped. They are scoped to particular templates. If we have a
buttons.css
file we would import it only into a buttons.js template and a.btn
class within would be inaccessible to some other template (e.g. forms.js), unless we imported it specifically there too.
With CSS Modules, it’s a guarantee that all the styles for a single component:
- Live in one place
- Only apply to that component and nothing else
Plus, any component can have a true dependency, like:
import buttons from './buttons.css'
import padding from './padding.css'
element.innerHTML = `<div class="${buttons.red} ${padding.large}">`
This approach is designed to fix the problem of the global scope in CSS.
In this repo, SCSS and its modules have been used with TailwindCSS for styling. But webpack5 and postcss configuration have been done with sass-loader, postcss-loader plugins. (So you are also free to use SASS files.)
First, I created tailwind.config.js
and postcss.config.js
files. Then I added blocks which are below:
// tailwind.config.js
module.exports = {
content: ['./pages/**/*.{js,ts,jsx,tsx}', './src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {},
},
plugins: [],
}
// postcss.config.js
module.exports = {
plugins: ['tailwindcss', 'autoprefixer', 'tailwindcss/nesting'],
}
The most configuration takes my time was StoryBook configuration because I wanted to integrate StoryBook to Tailwind CSS that is able to be compiled in SASS/SCSS files.
Here are addons
in .storybook/main.js
file:
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/preset-scss', // added later
{ // added later
name: '@storybook/addon-postcss',
options: {
cssLoaderOptions: {
importLoaders: 1,
},
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
],
And webpackFinal
:
webpackFinal: async (config) => {
config.resolve.alias = {
...config.resolve?.alias,
'@': [
require('path').resolve(__dirname, '../src/'),
require('path').resolve(__dirname, '../'),
],
}
config.resolve.roots = [
require('path').resolve(__dirname, '../public'),
'node_modules',
]
config.module.rules.push({
// this rule is for using TailwindCSS in .sass and .scss files.
test: /\.s[ac]ss$/,
use: [
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [require('tailwindcss'), require('autoprefixer')],
},
},
},
{
loader: 'sass-loader',
options: {
implementation: require('sass'),
},
},
],
include: require('path').resolve(__dirname, '../'),
})
return config
}
Here is .storybook/preview.js
:
import '../src/styles/globals.scss' //For using Tailwind CSS in global scope for StoryBook
import * as NextImage from 'next/image' // For previewing images in StoryBook
const OriginalNextImage = NextImage.default
Object.defineProperty(NextImage, 'default', {
configurable: true,
value: (props) => <OriginalNextImage {...props} unoptimized />,
})
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
previewTabs: {
'storybook/docs/panel': { index: -1 },
},
}
ESLint and Prettier configurations have been done in .eslintrc.json
, .prettierrc.json
files and .vscode
folder.
There are not much rules, so you're free to change the rules.
You need to enable Husky:
yarn husky install
Then a folder is going to be created under the .husky
folder named as "_
".
Husky "pre commit
" configuration have been done in .husky/pre-commit
and lint-stage configuration have been done in lint-staged.config.js
.
Now, when you use git commit -m "something"
command, it will run lint-staged scripts according to configuration in lint-staged.config.js
if type checking and/or linting is necessary.
I've been using yarn therefore there is yarn.lock file, I recommend you to use yarn for this repo.
If you've never used amplify before
- Sign up for an AWS account
- Install the AWS Amplify cli:
npm install -g @aws-amplify/cli
- Configure the Amplify cli
amplify configure
$ amplify init
# <Interactive>
? Enter a name for the project <PROJECT_NAME>
? Enter a name for the environment: dev (or whatever you would like to call this env)
? Choose your default editor: <YOUR_EDITOR_OF_CHOICE>
? Choose the type of app that you're building (Use arrow keys)
android
ios
❯ javascript
? What javascript framework are you using react
? Source Directory Path: src
? Distribution Directory Path: out
? Build Command: (npm run-script build)
? Start Command: (npm run-script start)
? Do you want to use an AWS profile? Y
# </Interactive>
$ amplify add api
# <Interactive>
? Please select from one of the below mentioned services (Use arrow keys)
❯ GraphQL
REST
? Provide API name: <API_NAME>
? Choose an authorization type for the API (Use arrow keys)
❯ API key
Amazon Cognito User Pool
? Do you have an annotated GraphQL schema? (y/N) y
? Provide your schema file path: ./schema.graphql
# </Interactive>
$ amplify push
# <Interactive>
? Are you sure you want to continue? Y
? Do you want to generate code for your newly created GraphQL API? Y
? Choose the code generation language target (Use arrow keys)
❯ javascript
typescript
flow
? Enter the file name pattern of graphql queries, mutations and subscriptions (src/graphql/**/*.js)
? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions (Y/n) Y
? Enter maximum statement depth [increase from default if your schema is deeply nested] (2)
# </Interactive>
npm install
# or
yarn
Now you can run development server:
npm run dev
# or
yarn dev
Or run the StoryBook:
npm run storybook
# or
yarn storybook
If you've run development server, open http://localhost:3000 with your browser to see the result.
If you've run the StoryBook, then it's going to open itself automatically on the port 6006.
You can start editing the page by modifying pages/index.tsx
. The page auto-updates as you edit the file.
API routes can be accessed on http://localhost:3000/api/hello. This endpoint can be edited in pages/api/hello.ts
.
The pages/api
directory is mapped to /api/*
. Files in this directory are treated as API routes instead of React pages.
This example shows how to build a server rendered web application with NextJS and AWS Amplify. We use AWS Amplify to generate code and to manage and consume the AWS cloud resources needed for our app. The NextJS app has dynamic and static routes to demonstrate how to load data on the server based on the incoming request.
There are two routes that are implemented:
/
: A static route that usesgetStaticProps
to load data from AppSync and renders it on the server (Code in pages/index(amplify-example).js)/todo/[id]
: A dynamic route that usesgetServerSideProps
and the id from the provided context to load a single todo from AppSync and render it on the server. (Code in pages/todo(amplify-example)/:[id].js)
The folders or files that contain (amplify-example)
in their names aren't tested yet. These files/folders have taken from Next.js example which is using aws-amplify
- Open
amplify/backend/api/projectname/schema.graphql
and change what you need to. - Run
amplify push
- 👍
Make sure to commit your changes before doing this.
mv amplify/backend/api/yourprojectname/schema.graphql ./schema.graphql
rm -rf amplify/ src/
amplify init
amplify add api
rm ./schema.graphql
amplify push
To learn more about mentione tech stack, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
- AWS Amplify Guide for React - AWS Amplify Guide for React.
- AWS Amplify Guide for Next.js - AWS Amplify Guide for Next.js.
- A Next.js and AWS Amplify Blog Post - A blog post about hosting a Next.js SSR app on AWS Amplify.
- Review Example Project - an example of Next.js with AWS Amplify.
- ESLint, Prettier, Husky and lint-staged Configuration - an example of using together ESLint, Prettier, Husky and lint-staged for code typing rules and typescript errors.
- How to Use Sass with CSS Modules in Next.js - an example of using together CSS modules, SASS and Next.js.
- What is AWS Amplify? - A resource of this repo's Readme.md
- Introduction to Tailwind CSS - A resource of this repo's Readme.md
- What is SASS? - A resource of this repo's Readme.md
- What is the difference between SCSS and SASS? - A resource of this repo's Readme.md
- What are CSS Modules and why do we need them? - A resource of this repo's Readme.md
- A little help - A little help for creating Readme file for this repo