├── .development.env ├── .dockerignore ├── .eslintrc.js ├── .github └── workflows │ ├── docker.yml │ └── test.yml ├── .gitignore ├── Dockerfile ├── README.md ├── babel.config.js ├── client ├── app.jsx ├── assets │ ├── favicon.ico │ └── robots.txt ├── components │ ├── Footer │ │ ├── Footer.jsx │ │ └── Footer.test.js │ ├── Header │ │ ├── Header.jsx │ │ └── Header.test.js │ └── Loader │ │ ├── Loader.css │ │ ├── Loader.jsx │ │ └── Loader.test.js ├── environment │ ├── .env │ └── README.md ├── global.css ├── index.html ├── index.jsx └── views │ ├── About.jsx │ ├── About.test.js │ ├── Home.jsx │ └── Home.test.js ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── server ├── __tests__ │ └── demo.js ├── app.js └── index.js ├── tailwind.config.js └── vite.config.js /.development.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/harrisoncramer/fullstack-template/788c0178519614de19a9088624a66bed1ad722a6/.development.env -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .git 3 | .development.env 4 | Dockerfile 5 | README.md 6 | .gitignore 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | node: true, 6 | jest: true, 7 | }, 8 | extends: ['eslint:recommended', 'plugin:react/recommended'], 9 | parserOptions: { 10 | ecmaFeatures: { 11 | jsx: true, 12 | }, 13 | ecmaVersion: 12, 14 | sourceType: 'module', 15 | }, 16 | plugins: ['react'], 17 | rules: {}, 18 | }; 19 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Build and publish docker image 2 | on: 3 | pull_request: 4 | branches: 5 | - main 6 | jobs: 7 | push_to_registry: 8 | name: Push Docker image to Docker Hub 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Check out the repo 12 | uses: actions/checkout@v2 13 | - name: Log in to Docker Hub 14 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 15 | with: 16 | username: ${{ secrets.DOCKER_USERNAME }} 17 | password: ${{ secrets.DOCKER_PASSWORD }} 18 | - name: Extract metadata (tags, labels) for Docker 19 | id: meta 20 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 21 | with: 22 | images: kingofcramers/fullstack-template 23 | - name: Build and push Docker image 24 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 25 | with: 26 | context: . 27 | push: true 28 | tags: ${{ steps.meta.outputs.tags }} 29 | labels: ${{ steps.meta.outputs.labels }} 30 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | on: 3 | pull_request: 4 | branches: ['main'] 5 | push: 6 | branches: ['dev', 'main'] 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Setup NodeJS 13 | uses: actions/setup-node@v2 14 | with: 15 | node-version: ${{ matrix.node-version }} 16 | cache: npm 17 | - run: npm ci 18 | - run: npm run test 19 | push_to_registry: 20 | name: Push Docker image to Docker Hub 21 | needs: test 22 | if: github.ref == 'refs/heads/main' 23 | runs-on: ubuntu-latest 24 | steps: 25 | - name: Check out the repo 26 | uses: actions/checkout@v2 27 | - name: Log in to Docker Hub 28 | uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 29 | with: 30 | username: ${{ secrets.DOCKER_USERNAME }} 31 | password: ${{ secrets.DOCKER_PASSWORD }} 32 | - name: Extract metadata (tags, labels) for Docker 33 | id: meta 34 | uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 35 | with: 36 | images: kingofcramers/fullstack-template 37 | - name: Build and push Docker image 38 | uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc 39 | with: 40 | context: . 41 | push: true 42 | tags: ${{ steps.meta.outputs.tags }} 43 | labels: ${{ steps.meta.outputs.labels }} 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .DS_Store 4 | .production.env 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ## Build layer 2 | FROM node:16.0.0 AS builder 3 | WORKDIR /app 4 | COPY package*.json . 5 | RUN npm install 6 | COPY . . 7 | RUN npm run build 8 | 9 | ## Production layer 10 | FROM alpine 11 | WORKDIR /app 12 | COPY --from=builder /app/package*.json . 13 | COPY --from=builder /app/build build 14 | COPY --from=builder /app/server server 15 | RUN apk add --update nodejs npm 16 | RUN npm install --only=prod 17 | RUN addgroup -S app && adduser -S prod -G app 18 | USER prod 19 | CMD ["npm", "run", "serve"] 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Template Repository 2 | 3 | This is a template repository for a full-stack, dockerized application using React, Vite, Express, and Tailwind. It includes: 4 | 5 | - Vite for fast reloads 6 | - Tailwind for utility-first styling 7 | - Express for SPA backend 8 | - React-router for routes 9 | - Lazy-loading for optimization 10 | - Supertest for backend tests 11 | - React-Testing-Library for frontend tests 12 | - Github Actions for CI\*\* 13 | - Docker for containerization 14 | 15 | ## Installation 16 | 17 | `npm install` 18 | 19 | ## Development 20 | 21 | Full Dev: `npm run start` 22 | 23 | Backend only: `npm run start:express` 24 | 25 | Frontend only: `npm run start:vite` 26 | 27 | ## Tests 28 | 29 | `npm run test` 30 | 31 | ## Docker 32 | 33 | Build the image: `docker build -t yourusername/yourapp . ` 34 | 35 | Run the image (map the ports): `docker run -dit -p 3000:3000 yourusername/yourapp` 36 | 37 | ## Github Actions 38 | 39 | The github actions in this repository are configured to build and push a Docker image to my account by default. You can either delete the `.github` folder or configure them with your own account and credentials. You may also delete the docker job, leaving the other CI tests in place. 40 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: ["babel-plugin-transform-vite-meta-env"], 3 | presets: [ 4 | [ 5 | "@babel/preset-env", 6 | { 7 | targets: { 8 | node: "current", 9 | }, 10 | }, 11 | ], 12 | "@babel/preset-react", 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /client/app.jsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense, lazy } from "react"; 2 | import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; 3 | import Header from "./components/Header/Header"; 4 | import Footer from "./components/Footer/Footer"; 5 | import Loader from "./components/Loader/Loader"; 6 | 7 | const Home = lazy(() => import("./views/Home")); 8 | const About = lazy(() => import("./views/About")); 9 | 10 | import "./global.css"; 11 | 12 | const App = () => { 13 | return ( 14 | 15 |
16 | }> 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |