├── .circleci └── config.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __mocks__ └── mocks.js ├── assets └── samples.png ├── babel.config.js ├── enzyme.js ├── jest.tsconfig.json ├── next.config.js ├── package.json ├── pages ├── ModuleCss.tsx ├── SSR.tsx ├── StyledJsx.tsx ├── _app.tsx ├── _document.tsx └── index.tsx ├── postcss.config.js ├── src ├── @types │ ├── global.d.ts │ ├── react.d.ts │ ├── styled-jsx-css.d.ts │ └── styled-jsx.d.ts ├── components │ ├── Color.css │ ├── Footer.tsx │ ├── Header.tsx │ ├── Home.css │ ├── Home.spec.tsx │ ├── Home.tsx │ ├── Layout.tsx │ └── Nav.tsx ├── constants │ └── env.ts ├── lib │ ├── index.ts │ └── timer.ts ├── redux │ ├── common.ts │ ├── index.ts │ ├── log │ │ └── index.ts │ ├── persist │ │ └── index.ts │ ├── router │ │ └── index.ts │ └── system │ │ └── index.ts └── store.ts ├── static └── favicon.ico ├── tsconfig.json ├── tslint.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | branches: 5 | only: 6 | - master 7 | docker: 8 | - image: deptno/circleci-awscli-node8:0.1 9 | working_directory: ~/repo 10 | 11 | steps: 12 | - checkout 13 | - restore_cache: 14 | key: v1-npm-deps-{{ checksum "package.json" }} 15 | - run: yarn 16 | - run: yarn ts:check 17 | - run: yarn test:coverage 18 | - save_cache: 19 | key: v1-npm-deps-{{ checksum "package.json" }} 20 | paths: 21 | - node_modules 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .next 2 | .idea 3 | .vscode 4 | out 5 | pages/**/*.js 6 | pages/**/*.map 7 | pages/**/*.jsx 8 | src/**/*.js 9 | src/**/*.map 10 | src/**/*.jsx 11 | storybook-static 12 | .DS_Store 13 | 14 | # Created by .ignore support plugin (hsz.mobi) 15 | ### Node template 16 | # Logs 17 | logs 18 | *.log 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | # Runtime data 24 | pids 25 | *.pid 26 | *.seed 27 | *.pid.lock 28 | 29 | # Directory for instrumented libs generated by jscoverage/JSCover 30 | lib-cov 31 | 32 | # Coverage directory used by tools like istanbul 33 | coverage 34 | 35 | # nyc test coverage 36 | .nyc_output 37 | 38 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 39 | .grunt 40 | 41 | # Bower dependency directory (https://bower.io/) 42 | bower_components 43 | 44 | # node-waf configuration 45 | .lock-wscript 46 | 47 | # Compiled binary addons (http://nodejs.org/api/addons.html) 48 | build/Release 49 | 50 | # Dependency directories 51 | node_modules/ 52 | jspm_packages/ 53 | 54 | # Typescript v1 declaration files 55 | typings/ 56 | 57 | # Optional npm cache directory 58 | .npm 59 | 60 | # Optional eslint cache 61 | .eslintcache 62 | 63 | # Optional REPL history 64 | .node_repl_history 65 | 66 | # Output of 'npm pack' 67 | *.tgz 68 | 69 | # Yarn Integrity file 70 | .yarn-integrity 71 | 72 | # dotenv environment variables file 73 | .env 74 | 75 | 76 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # changelog 2 | 3 | ### 8.1.0 4 | - next@8 5 | - react@16.8.1 6 | - typescript@3.3.3 7 | 8 | ### 8.0.0 9 | - next@8 10 | - react@16.8.1 11 | - typescript@3.3.3 12 | 13 | ### 7.0.2 14 | - add favicon 15 | - next@7.0.2 16 | - react@16.7.0-alpha 17 | - typescript@3.2.1 18 | 19 | ### 7.0.1 20 | - replace npm with yarn in `README.me` 21 | - remove storybook 22 | - apply postcss-preset-env 23 | - typescript@3.1.1 24 | - next@7.0.1 25 | - react@16.5 26 | 27 | ### 6.1.1 28 | - typescript@3.0.1 29 | - next@6.1.1 30 | - react@16.4 31 | 32 | ### 6.1.0 33 | - remove bootstrap cdn 34 | - more examples 35 | - ssr 36 | - module css 37 | - style jsx 38 | - next.js way 39 | - custom _page.tsx => _app.tsx, 40 | 41 | ### 6.0.3 fetch 42 | - update postcss 43 | - add postcss-import 44 | - font-awesome@5.1.0 45 | - add module css test env 46 | - update readme 47 | - storybook 48 | - add head.html to load cdn 49 | - use @zeit/next-css for module css config 50 | - update packages 51 | - typescript@2.9.2 52 | - @zeit/next-typescript@1.1.0 53 | - fix import stylesheet, using module css 54 | - update cdn 55 | - bootstrap@4 56 | - fontawesome@5 57 | - fix storybook not working [issue#14](https://github.com/deptno/next.js-typescript-starter-kit/issues/14) 58 | - update packages 59 | - storybook@ 60 | - typescript@2.9.1(support jsx generic) 61 | 62 | ### 6.0.3 63 | - update packages 64 | - next@6.0.3 65 | - typescript@2.8.4 66 | - react@16.4 67 | - redux@4.0.0 68 | 69 | ### 6.0.1 70 | - unsupport naver analytics(unsafe) 71 | - update packages 72 | - next@6.0.1 73 | - typescript@2.8.3 74 | - ...etc@latest 75 | 76 | ### 5.1.0 77 | - update packages 78 | - typescript@2.7.2 79 | - next@5.1.0 80 | - ...etc@latest 81 | 82 | ### 5.0.5 83 | - ci 84 | - fix 85 | - Layout.tsx global style is not rendered in output [issue#7](https://github.com/deptno/next.js-typescript-starter-kit/issues/7) 86 | 87 | ### 5.0.4 88 | - update jest config 89 | - scripts 90 | - `npm run ts:check` # typescript error check 91 | - `npm run test:watch` 92 | - `npm run test:coverage` 93 | 94 | ### 5.0.3 95 | - fix 96 | - store-reducer connection 97 | - add enzyme 98 | - scripts 99 | - `npm run test` 100 | - `npm run test:watch` 101 | - rename dev script `npm run dev` > `npm run start:dev` 102 | 103 | ### 5.0.2 104 | - add storybook 105 | 106 | ### 5.0.1 107 | - name change 108 | - next.js-typescript-boilerplate -> next.js-typescript-starter-kit 109 | 110 | ### 5.0.0 111 | - package.json scripts 112 | - add `export`, to export static assets(MUST run after `npm run build`) 113 | - remove `ts:compile` script (no more need to run tsc) 114 | 115 | - apply new packages 116 | - @next/next-typescript 117 | 118 | - update packages 119 | - next@5.0.0 120 | - react@16.2 121 | - react-dom@16.2 122 | - typescript@2.7.1 123 | 124 | ### 4.1.4 125 | - update packages 126 | - next@4.1.4 127 | - react@16.1 128 | - react-dom@16.1 129 | - typescript@2.6.1 130 | - update packages client load 131 | - bootstrap@4.0.0-beta.2 132 | - version align with next 133 | 134 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Bonggyun Lee 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Next.js TypeScript Starter Kit [![CircleCI](https://circleci.com/gh/deptno/next.js-typescript-starter-kit.svg?style=svg)](https://circleci.com/gh/deptno/next.js-typescript-starter-kit) 2 | 3 | ![samples](assets/samples.png) 4 | 5 | > see **[ChangeLog](CHANGELOG.md)** 6 | 7 | ## Feature 8 | - TypeScript 9 | - Styled-jsx 10 | - Module css **(PostCSS - cssnext, nested, import)** 11 | - SEO & analytics(Google Analytics, Facebook Pixel, Naver Analytics) 12 | - ~~Storybook **(support module css)**~~ 13 | - Jest & Enzyme **(support module css)** 14 | 15 | ### Load from CDN 16 | - font-awesome@5 17 | 18 | ## Installation 19 | 20 | ```sh 21 | git clone https://github.com/deptno/next.js-typescript-starter-kit my-project 22 | cd my-project 23 | rm -r .git 24 | yarn 25 | ``` 26 | 27 | ## Run :rocket: 28 | 29 | #### :rocket: Test 30 | ```bash 31 | yarn test # test 32 | yarn test:watch 33 | yarn test:coverage # report coverage 34 | ``` 35 | 36 |
~~:rocket: StoryBook~~ 37 |

38 | 39 | #### :rocket: StoryBook 40 | ```bash 41 | yarn storybook # open browser localhost:6006 42 | ``` 43 | 44 | ```bash 45 | yarn build-storybook # Build storybook static assets 46 | ``` 47 | 48 |

49 |
50 | 51 | ### :rocket: Development 52 | 53 | ```bash 54 | yarn start:dev # run 55 | ``` 56 | 57 | ### :rocket: Production 58 | 59 | #### Serve 60 | ```bash 61 | yarn 62 | yarn build # create .next directory 63 | yarn start # start server 64 | ``` 65 | 66 | #### Build static HTML 67 | ```bash 68 | yarn 69 | yarn build # create .next directory 70 | yarn export # create .out directory 71 | ``` 72 | 73 | ## Configuration 74 | 75 | Set SEO & analytics variables 76 | 77 | > src/constants/env.ts 78 | 79 | ```typescript 80 | export const GA_TRACKING_ID = '' 81 | export const FB_TRACKING_ID = '' 82 | export const SENTRY_TRACKING_ID = '' 83 | 84 | // for meta tag 85 | export const SITE_NAME = '' 86 | export const SITE_TITLE = '' 87 | export const SITE_DESCRIPTION = '' 88 | export const SITE_IMAGE = '' 89 | ``` 90 | 91 | If each variable evaluated false, it does not load related library 92 | 93 | ## Usage 94 | 95 | ### Module CSS ([src/components/Home.tsx](src/components/Home.tsx)) 96 | 97 | ```typescript jsx 98 | import * as classnames from 'classnames' 99 | import * as css from './Home.css' 100 | 101 | export const Just = props =>
102 | export const Mixed = props =>
103 | ``` 104 | 105 | ### Styled-jsx 106 | 107 | #### Global scope ([src/components/Layout.tsx](src/components/Layout.tsx)) 108 | 109 | ```typescript jsx 110 | const Layout = props => 111 | 112 | 118 | 119 | ``` 120 | 121 | #### Local scope ([src/components/Home.tsx](src/components/Home.tsx)) 122 | 123 | ```typescript jsx 124 | export const Home = props => 125 |
126 | 129 | home 130 |
131 | ``` 132 | 133 | #### Others 134 | 135 | - styled-jsx/css [issue#2](https://github.com/deptno/next.js-typescript-starter-kit/issues/2) 136 | - external css, module [issue#3](https://github.com/deptno/next.js-typescript-starter-kit/issues/3) 137 | 138 | ### Related 139 | 140 | - [typescript-monorepo-next-example](https://github.com/deptno/typescript-monorepo-next-example) - Next.js version 141 | -------------------------------------------------------------------------------- /__mocks__/mocks.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; -------------------------------------------------------------------------------- /assets/samples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deptno/next.js-typescript-starter-kit/f3883099d9d860d8164ed1a0d2e6cf4a9af8bd96/assets/samples.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true) 3 | return { 4 | presets: [ 5 | 'next/babel', 6 | '@zeit/next-typescript/babel' 7 | ], 8 | plugins: [ 9 | '@babel/proposal-class-properties', 10 | '@babel/proposal-object-rest-spread', 11 | ['transform-define', { 12 | 'process.env.NODE_ENV': process.env.NODE_ENV 13 | }] 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /enzyme.js: -------------------------------------------------------------------------------- 1 | const Adapter = require('enzyme-adapter-react-16') 2 | 3 | require('enzyme').configure({adapter: new Adapter()}) 4 | -------------------------------------------------------------------------------- /jest.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "esnext", 5 | "jsx": "react", 6 | "sourceMap": false, 7 | "experimentalDecorators": true, 8 | "noImplicitUseStrict": true, 9 | "removeComments": true, 10 | "moduleResolution": "node", 11 | "lib": [ 12 | "es2017", 13 | "dom" 14 | ], 15 | "typeRoots": [ 16 | "node_modules/@types", 17 | "src/@types" 18 | ] 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | "out", 23 | ".next" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | const withTypescript = require('@zeit/next-typescript') 2 | const withCSS = require('@zeit/next-css') 3 | const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer') 4 | 5 | module.exports = withTypescript( 6 | withCSS({ 7 | webpack(config) { 8 | if (process.env.ANALYZE) { 9 | config.plugins.push(new BundleAnalyzerPlugin({ 10 | analyzerMode: 'server', 11 | analyzerPort: 8888, 12 | openAnalyzer: true 13 | })) 14 | } 15 | return config 16 | }, 17 | cssModules: true, 18 | }) 19 | ) 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next.js-typescript-starter-kit", 3 | "version": "8.1.0", 4 | "main": "index.js", 5 | "repository": "https://github.com/deptno/next.js-typescript-starter-kit", 6 | "author": "Bonggyun Lee ", 7 | "license": "MIT", 8 | "private": true, 9 | "scripts": { 10 | "start:dev": "next", 11 | "build:dev": "next build", 12 | "build": "NODE_ENV=production next build", 13 | "export": "next export", 14 | "analyze": "NODE_ENV=production ANALYZE=true next build", 15 | "start": "next start", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:coverage": "jest --coverage", 19 | "ts:check": "tsc --noEmit" 20 | }, 21 | "dependencies": { 22 | "classnames": "^2.2.6", 23 | "isomorphic-fetch": "^2.2.1", 24 | "moment": "^2.22.2", 25 | "next": "^8.1.0", 26 | "react": "^16.8.6", 27 | "react-dom": "^16.8.6", 28 | "react-ga": "^2.5.3", 29 | "react-redux": "^5.0.7", 30 | "redux": "^4.0.0", 31 | "redux-logger": "^3.0.6", 32 | "redux-persist": "^4.9.1", 33 | "redux-thunk": "^2.3.0" 34 | }, 35 | "devDependencies": { 36 | "@types/classnames": "^2.2.6", 37 | "@types/enzyme": "^3.1.10", 38 | "@types/enzyme-adapter-react-16": "^1.0.2", 39 | "@types/jest": "^24.0.11", 40 | "@types/node": "^8.10.14", 41 | "@types/react": "^16.7.13", 42 | "@types/react-redux": "^6.0.0", 43 | "@zeit/next-css": "^1.0.1", 44 | "@zeit/next-typescript": "^1.1.1", 45 | "babel-jest": "^24.7.1", 46 | "babel-plugin-transform-define": "^1.3.0", 47 | "enzyme": "^3.7.0", 48 | "enzyme-adapter-react-16": "^1.6.0", 49 | "jest": "^24.7.1", 50 | "postcss": "^7.0.5", 51 | "postcss-preset-env": "^6.0.10", 52 | "ts-jest": "^24.0.2", 53 | "typescript": "^3.4.5", 54 | "webpack-bundle-analyzer": "^3.0.2" 55 | }, 56 | "jest": { 57 | "moduleFileExtensions": [ 58 | "ts", 59 | "tsx", 60 | "js" 61 | ], 62 | "transform": { 63 | "^.+\\.tsx?$": "ts-jest" 64 | }, 65 | "testMatch": [ 66 | "**/*.(test|spec).(ts|tsx)" 67 | ], 68 | "globals": { 69 | "ts-jest": { 70 | "babelConfig": true, 71 | "tsConfig": "jest.tsconfig.json" 72 | } 73 | }, 74 | "coveragePathIgnorePatterns": [ 75 | "/node_modules/", 76 | "enzyme.js" 77 | ], 78 | "setupFilesAfterEnv": [ 79 | "/enzyme.js" 80 | ], 81 | "coverageReporters": [ 82 | "json", 83 | "lcov", 84 | "text", 85 | "text-summary" 86 | ], 87 | "moduleNameMapper": { 88 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/mocks.js", 89 | "\\.(css|less)$": "/__mocks__/mocks.js" 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /pages/ModuleCss.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import * as css from '../src/components/Home.css' 3 | import {Layout} from '../src/components/Layout' 4 | 5 | export default props => 6 | 7 |
8 | home 9 |
10 | nested 11 |
12 |
13 |
-------------------------------------------------------------------------------- /pages/SSR.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {Layout} from '../src/components/Layout' 3 | 4 | const SSR: React.NextFunctionComponent = props => 5 | 6 | {props.name} 7 | 8 | 9 | SSR.getInitialProps = async (context) => { 10 | const props = { 11 | name: '"next.js-typescript-starter-kit" from client' 12 | } 13 | const server = !!context.req 14 | 15 | if (server) { 16 | props.name = await mockFetchName() 17 | } 18 | 19 | return props 20 | } 21 | export default SSR 22 | 23 | interface Props { 24 | name 25 | } 26 | async function mockFetchName() { 27 | return '"next.js-typescript-starter-kit" from server' 28 | } 29 | -------------------------------------------------------------------------------- /pages/StyledJsx.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import {Layout} from '../src/components/Layout' 3 | 4 | export default props => 5 | 6 |
7 | {/*language=PostCSS*/} 8 | 14 | StyledJsx 15 |
16 |
-------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import 'isomorphic-fetch' 2 | import * as React from 'react' 3 | import {Provider} from 'react-redux' 4 | import App, {Container} from 'next/app' 5 | import {getStore} from '../src/store' 6 | 7 | export default class extends App { 8 | static async getInitialProps({Component, router, ctx}) { 9 | const server = !!ctx.req 10 | const store = getStore(undefined, server) 11 | const state = store.getState() 12 | const out = {state, server} as any 13 | 14 | if (Component.getInitialProps) { 15 | return { 16 | ...out, 17 | pageProps: { 18 | ...await Component.getInitialProps(ctx) 19 | } 20 | } 21 | } 22 | 23 | return out 24 | } 25 | 26 | render() { 27 | const {props} = this as any 28 | const {Component, pageProps} = props 29 | 30 | return ( 31 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react' 2 | import Document, {Head, Main, NextScript} from 'next/document' 3 | import { 4 | DEV, FB_TRACKING_ID, SENTRY_TRACKING_ID, SITE_DESCRIPTION, SITE_IMAGE, 5 | SITE_NAME, SITE_TITLE 6 | } from '../src/constants/env' 7 | 8 | export default class extends Document { 9 | static async getInitialProps(...args) { 10 | const documentProps = await Document.getInitialProps(...args) 11 | const {req, renderPage} = args[0] 12 | const page = renderPage() 13 | 14 | return {...documentProps, ...page} 15 | } 16 | 17 | render() { 18 | return ( 19 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | {!DEV && FB_TRACKING_ID && ( 47 |