Setting up Next.js with Tailwindcss and Docker

I’m constantly re-evaluating my client-side technology stacks in the quest for that perfect setup. Perfection is an impossible goal, but I think I’ve gotten a little closer with this tech pairing. Here’s a brief description:

  • Next.js - Web framework supporting client-side, server-side, and hybrid server rendering, smart bundling, route pre-fetching, and more.
  • Tailwindcss - CSS framework designed to rapidly build modern websites without leaving HTML.
  • Docker - Containerization platform that enables you to create, deploy, and run applications in an isolated environment.

Although I've used React before, I was always annoyed with the number of additional libraries required to create a production application. For this reason, I've generally stuck with Vue.js or Angular. Next.js, however, supercharges React. It's a very impressive project. On the styling front, people start with some framework like Bootstrap, either using it in its entirety, or taking it's reset, grid, and helpers to jumpstart a project. Tailwindcss seems to be the next evolution. All of the utilities while only compiling and including what's being used.

Using Next.js together with Tailwindcss has the potential to create websites very rapidly. React as a component-based library works very well with Tailwindcss for building components. There's essentially no CSS required to build pixel-perfect websites. Very little server cruft mixed with sharing components, views, and API calls client and server-side blurs the lines of frontend and backend development. Finally, Docker makes it easy to package and deploy the site to staging and production.

So let's set up a site. Make sure you have Node.js installed, and open a terminal.

Next.js

Next.js has an easy npx package to set up almost everything automatically:

npx create-next-app

This will ask for the project name and then install dependencies. At this point, the project runs. I'd run it once to make sure everything works:

cd project-name
npm run dev

You should see the application by navigating your browser to http://localhost:3000. CTRL+C stops the server.

I'm not too fond of the source code in the root folder. Luckily, Next.js natively supports the src folder standard. I tend to set that up real quick, so I don't have to worry about it later:

mkdir src
mv pages src
mv styles src

Rerun the application again to verify everything still works:

npm run dev

At this point, the Next.js is good to go. Now it's time to supercharge the frontend using Tailwindcss.

Tailwindcss

We're using the documented Tailwindcss installation instructions for postcss:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init

This command installs all the Tailwindcss dependencies and creates the tailwind.config.js. Update the tailwind.config.js to work with the new source location:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{html,js,jsx,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

Everything wires together by creating a postcss.config.js file in the root folder:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

Open the src/styles/globals.css and update to include tailwind:

@tailwind base;
@tailwind components;

/* Your own custom component styles */

@tailwind utilities;

Update the src/pages/index.js with some tailwind syntax:

export default function Home() {
  return <h1 className="text-3xl font-bold underline">Hello world!</h1>
}

Now, run the application to make sure it's still working:

npm run dev

Congratulations, the project is now working with Next.js and Tailwindcss. The final step is to build a Docker image so the site can deploy to an environment. Make sure Docker is installed and running on your machine.

Docker

Getting docker support is pretty easy. I got the original Dockerfile from https://github.com/vercel/next.js/tree/canary/examples/with-docker. I've updated it to work specifically for npm, because it works with yarn by default. The first step is to update your next.config.js to build as a standalone app:

// next.config.js
module.exports = {
  // ... rest of the configuration.
  output: 'standalone',
}

Now you can create a Dockerfile in the root directory. I've updated the configuration to work specifically with npm. If you're using yarn, then go to https://github.com/vercel/next.js/tree/canary/examples/with-docker and use that configuration.

FROM node:16-alpine AS deps

RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm install

# Rebuild the source code only when needed
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .

RUN npm run build

# Production image, copy all the files and run next
FROM node:16-alpine AS runner
WORKDIR /app

ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/next.config.js ./
COPY --from=builder /app/public ./public
COPY --from=builder /app/package.json ./package.json

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT 3000

CMD ["node", "server.js"]

This Dockerfile rebuilds the source only if needed and has a few tricks in it to act more efficiently. Run it to make sure it works:

docker build -t test-next-tailwind .
docker run -p 3000:3000 test-next-tailwind

Navigating to localhost should show the application running from a Docker container.

As you can see, creating a working site ready for React templates and easy styling doesn't take much. I'll evaluate the solution on an actual production application and give my genuine opinion. I'm also adding testing and will probably write an article about that process. A working demo is at https://github.com/mattkruskamp/nextjs-tailwindcss. Happy coding.