Tailwind V3 Astro Setup
Astro + Tailwind CSS + React Template
Section titled “Astro + Tailwind CSS + React Template”A streamlined project template for building high-performance websites with Astro, Tailwind CSS v3, and React support.
Features
Section titled “Features”- ⚡️ Astro - Blazing fast static site generator with Island Architecture
- 💨 Tailwind CSS v3 - Utility-first CSS framework
- ⚛️ React - Component-based UI library for dynamic features
- 📝 Markdown Support - Write content in Markdown
- 🧹 Linting - ESLint, Prettier, and Stylelint configuration
- 🔍 TypeScript - Type checking and code intelligence
- 📱 Responsive - Mobile-first design approach
- 🚀 Optimized - Built-in performance optimization
Getting Started
Section titled “Getting Started”Prerequisites
Section titled “Prerequisites”- Node.js 16.x or higher
- npm or yarn
Installation
Section titled “Installation”-
Clone this repository
Terminal window git clone https://github.com/yourusername/astro-tailwind-template.git my-projectcd my-project -
Install dependencies
Terminal window npm install# oryarn install -
Start the development server
npm run dev# oryarn dev
- Open your browser and visit
http://localhost:3000
Project Structure
Section titled “Project Structure”/├── .github/ # GitHub configuration├── public/ # Static assets│ ├── favicon.svg│ └── robots.txt├── src/│ ├── components/ # Reusable components│ │ ├── astro/ # Astro components│ │ └── react/ # React components│ ├── layouts/ # Page layouts│ ├── pages/ # Page routes and endpoints│ │ ├── posts/ # Markdown blog posts│ │ └── index.astro # Homepage│ ├── styles/ # Global styles│ └── utils/ # Utility functions├── .eslintrc.json # ESLint configuration├── .prettierrc.json # Prettier configuration├── .stylelintrc.json # Stylelint configuration├── astro.config.mjs # Astro configuration├── tailwind.config.js # Tailwind CSS configuration└── tsconfig.json # TypeScript configuration
Configuration
Section titled “Configuration”Astro Configuration
Section titled “Astro Configuration”The Astro configuration is in astro.config.mjs
:
import { defineConfig } from 'astro/config';import tailwind from '@astrojs/tailwind';import react from '@astrojs/react';
export default defineConfig({ integrations: [tailwind(), react()], markdown: { shikiConfig: { theme: 'github-dark', wrap: true, }, },});
Tailwind CSS Configuration
Section titled “Tailwind CSS Configuration”The Tailwind CSS configuration is in tailwind.config.js
:
/** @type {import('tailwindcss').Config} */module.exports = { content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], theme: { extend: { fontFamily: { sans: ['Roboto', 'sans-serif'], mono: ['Roboto Mono', 'monospace'], slab: ['Roboto Slab', 'serif'], }, }, }, plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')],};
ESLint Configuration
Section titled “ESLint Configuration”The ESLint configuration is in .eslintrc.json
:
{ "env": { "browser": true, "es2021": true, "es2022": true, "es6": true, "jest": true, "jquery": true, "node": true }, "extends": [ "airbnb-base", "plugin:jsx-a11y/recommended", "plugin:mdx/recommended", "eslint:recommended", "plugin:astro/recommended", "plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:@typescript-eslint/recommended", "prettier" ], "globals": { "$": true, "jQuery": true }, "overrides": [ { "files": ["*.astro"], "parser": "astro-eslint-parser", "parserOptions": { "extraFileExtensions": [".astro"], "jsx": true, "parser": "@typescript-eslint/parser", "rules": { // Override rules for Astro files } }, "rules": { // Custom rules "astro/no-conflict-set-directives": "error", "astro/no-unused-define-vars-in-style": "error", "jsx-a11y/anchor-is-valid": "off", "jsx-a11y/heading-has-content": "off", "jsx-a11y/no-noninteractive-element-to-interactive-role": "off", "react/no-unknown-property": [ "error", { "ignore": [ "class", "set:html", "define:vars", "is:raw", "transition:animate", "transition:fade", "transition:slide", "transition:persist", "clip-rule" ] } ] } }, { "files": ["*.mdx"], "parser": "eslint-mdx", "rules": { "@typescript-eslint/no-unused-vars": "off", "no-unused-expressions": "off", "react/jsx-no-undef": "off" } }, { "excludedFiles": [ "assets/js/bundle.js", "assets/dist/**", "stencil.conf.js", "webpack.*.js", "**/*.min.js", "**/*.min.js.map" ], "files": ["*.js", "*.jsx", "*.ts", "*.tsx"] }, { "env": { "node": true }, "files": ["webpack.*.js", "*.config.js", "*.config.mjs", "*.config.ts", "*.config.js", "astro.config.mjs"], "parserOptions": { "sourceType": "module" }, "rules": { "import/no-extraneous-dependencies": "off" } } ], "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaFeatures": { "jsx": true }, "ecmaVersion": "latest", "sourceType": "module" }, "plugins": ["@typescript-eslint", "prettier", "react-hooks", "jsx-a11y", "react", "@typescript-eslint"], "root": true, "rules": { "@typescript-eslint/consistent-type-imports": [ "warn", { "prefer": "type-imports" } ], "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-shadow": "warn", "@typescript-eslint/no-unused-vars": [ "warn", { "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_", "varsIgnorePattern": "^_" } ], "@typescript-eslint/no-use-before-define": "error", "@typescript-eslint/no-var-requires": "off", "block-scoped-var": 0, "class-methods-use-this": 0, "consistent-return": 0, "default-case": 0, "default-param-last": 0, "eqeqeq": ["warn", "smart"], "func-names": 0, "global-require": "off", "import/extensions": [ "error", "ignorePackages", { "astro": "always", "js": "never", "json": "always", "jsx": "never", "mdx": "never", "ts": "never", "tsx": "never" } ], "import/first": 0, "import/no-cycle": 0, "import/no-named-as-default": 0, "import/no-named-as-default-member": 0, "import/no-unresolved": [ "error", { "ignore": [ "@astrojs/image/components", "^astro:", "^@/", "astro/config", "@astrojs/react", "@astrojs/tailwind", "@astrojs/cloudflare", "@astrojs/partytown" ] } ], "import/order": "off", "import/prefer-default-export": "off", "jsx-a11y/anchor-is-valid": [ "error", { "aspects": ["invalidHref", "preferButton"], "components": ["Link"], "specialLink": ["to", "hrefLeft", "hrefRight"] } ], "jsx-a11y/no-redundant-roles": "off", "linebreak-style": ["error", "unix"], "max-classes-per-file": 0, "max-len": 0, "max-lines": "off", "new-cap": 0, "newline-per-chained-call": 0, "no-alert": 0, "no-cond-assign": 0, "no-const-assign": 0, "no-constructor-return": 0, "no-empty-function": 0, "no-inner-declarations": 0, "no-loop-func": 0, "no-mixed-operators": 0, "no-mixed-spaces-and-tabs": 2, "no-multi-assign": 0, "no-multi-str": 0, "no-new": 0, "no-param-reassign": 0, "no-plusplus": 0, "no-prototype-builtins": 0, "no-redeclare": 0, "no-restricted-globals": 0, "no-restricted-syntax": 0, "no-shadow": "off", "no-template-curly-in-string": 0, "no-undef": 0, "no-undef-init": 0, "no-underscore-dangle": 0, "no-unused-expressions": 0, "no-unused-vars": "off", "no-use-before-define": "off", "no-useless-concat": 0, "no-useless-constructor": 0, "no-useless-escape": 0, "no-useless-return": 0, "no-var": "error", "object-shorthand": 0, "one-var": 0, "one-var-declaration-per-line": 0, "operator-assignment": 0, "prefer-arrow-callback": 0, "prefer-const": "warn", "prefer-destructuring": 0, "prefer-rest-params": 0, "prefer-spread": 0, "prettier/prettier": [ "error", { "printWidth": 120, "singleQuote": true, "tabWidth": 4, "trailingComma": "all" } ], "quote-props": 0, "quotes": [ "error", "single", { "avoidEscape": true } ], "react-hooks/exhaustive-deps": "warn", "react-hooks/rules-of-hooks": "error", "react/no-unescaped-entities": "off", "react/prop-types": "off", "react/react-in-jsx-scope": "off", "semi": ["error", "always"], "simple-import-sort/exports": "warn", "simple-import-sort/imports": "off", "sonarjs/cognitive-complexity": 0, "sonarjs/prefer-optional-chain": 0, "sort-imports": "off", "vars-on-top": 0 }, "settings": { "import/resolver": { "node": { "extensions": [".js", ".jsx", ".ts", ".tsx", ".astro"] } }, "mdx": { "code-blocks": true }, "react": { "version": "detect" } }}
Prettier Configuration
Section titled “Prettier Configuration”The Prettier configuration is in .prettierrc.json
:
{ "arrowParens": "avoid", "bracketSameLine": true, "bracketSpacing": true, "embeddedLanguageFormatting": "auto", "endOfLine": "lf", "experimentalOperatorPosition": "end", "experimentalTernaries": true, "htmlWhitespaceSensitivity": "css", "jsxBracketSameLine": false, "objectWrap": "preserve", "overrides": [ { "files": ["*.astro"], "options": { "parser": "astro", "printWidth": 120, "tabWidth": 4 } }, { "files": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.mjs"], "options": { "parser": "babel-ts", "printWidth": 120, "singleQuote": true, "tabWidth": 4 } }, { "files": ["*.handlebars", "*.hbs"], "options": { "parser": "glimmer", "printWidth": 120, "singleQuote": false } }, { "files": "*.html", "options": { "embeddedLanguageFormatting": "off", "htmlWhitespaceSensitivity": "strict", "parser": "html", "printWidth": 2000, "singleAttributePerLine": true, "singleQuote": false, "tabWidth": 4 } }, { "files": "*.md", "options": { "parser": "markdown", "printWidth": 120 } }, { "files": ["*.scss", "*.less", "*.sass", "*.css"], "options": { "parser": "scss", "printWidth": 200, "singleQuote": true, "tabWidth": 4 } }, { "files": ["*.json", "*.jsonc"], "options": { "parser": "json", "printWidth": 120, "trailingComma": "none" } }, { "files": ["*.yaml", "*.yml"], "options": { "parser": "yaml", "printWidth": 120, "tabWidth": 2 } } ], "plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss-extra-plus", "prettier-plugin-tailwindcss"], "printWidth": 100, "proseWrap": "preserve", "quoteProps": "as-needed", "semi": true, "singleAttributePerLine": false, "singleQuote": true, "tabWidth": 2, "tailwindConfig": "tailwind.config.mjs", "trailingComma": "es5", "useTabs": false, "vueIndentScriptAndStyle": true}
Stylelint Configuration
Section titled “Stylelint Configuration”The Stylelint configuration is in .stylelintrc.json
:
{ "extends": ["stylelint-config-standard", "stylelint-config-tailwindcss"], "rules": { "at-rule-no-unknown": [ true, { "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen", "layer"] } ], "declaration-block-trailing-semicolon": null, "no-descending-specificity": null }}
Scripts
Section titled “Scripts”Package.json scripts:
{ "scripts": { "build": "astro build", "check": "astro check && tsc --noEmit", "dev": "astro dev", "format": "prettier --write .", "lint": "npm run lint:js && npm run lint:style", "lint:js": "eslint . --ext .js,.jsx,.ts,.tsx,.astro", "lint:style": "stylelint \"src/**/*.{css,scss,astro}\"", "preview": "astro preview", "start": "astro dev" }}
Using React Components
Section titled “Using React Components”Create React components in the src/components/react/
directory:
import { useState } from 'react';
export default function Counter() { const [count, setCount] = useState(0);
return ( <div className='py-4'> <p className='text-lg font-medium'>Count: {count}</p> <button onClick={() => setCount(count + 1)} className='mt-2 rounded bg-blue-600 px-4 py-2 text-white hover:bg-blue-700'> Increment </button> </div> );}
Use the React component in an Astro page:
---import Layout from '../layouts/Layout.astro';import Counter from '../components/react/Counter';---
<Layout title='Home'> <main class='mx-auto max-w-4xl p-4'> <h1 class='mb-4 text-4xl font-bold'>Welcome to Astro with React and Tailwind CSS</h1> <p class='mb-4'>This is a template for building websites with Astro, React, and Tailwind CSS.</p> <Counter client:visible /> </main></Layout>
Working with Markdown
Section titled “Working with Markdown”Create Markdown content in the src/pages/posts/
directory:
---layout: ../../layouts/BlogPostLayout.astrotitle: Getting Started with AstropublishDate: 2023-05-15description: Learn how to get started with Astro and this template---
# Getting Started with Astro
This is a sample blog post written in Markdown.
## Features
- **Fast**: Astro builds websites that ship less JavaScript- **Flexible**: Supports React, Vue, Svelte, and more- **UI-agnostic**: Use any frontend framework you want
Create a blog post layout:
---import Layout from './Layout.astro';
const { frontmatter } = Astro.props;---
<Layout title={frontmatter.title}> <article class='mx-auto max-w-3xl p-4'> <h1 class='mb-2 text-3xl font-bold'>{frontmatter.title}</h1> <p class='mb-6 text-gray-500'>Published on {frontmatter.publishDate}</p> <div class='prose prose-lg max-w-none'> <slot /> </div> </article></Layout>
Deployment
Section titled “Deployment”Build the Project
Section titled “Build the Project”npm run build# oryarn build
The build output will be in the dist/
directory, which you can deploy to your hosting platform of choice.
Deployment with Cloudflare Pages
Section titled “Deployment with Cloudflare Pages”This template is optimized for deployment on Cloudflare Pages:
-
Push your code to GitHub or GitLab repository
-
Connect your repository to Cloudflare Pages:
- Log in to the Cloudflare dashboard
- Go to Pages > Create a project
- Select your repository and set up the project
-
Configure build settings:
- Build command:
npm run build
- Build output directory:
dist
- Node.js version:
16
(or newer)
- Build command:
-
Environment variables (optional):
- You can add environment variables in the Cloudflare Pages dashboard
-
Deploy:
- Cloudflare Pages will automatically build and deploy your site
- Each commit to your main branch will trigger a new production deployment
- Pull requests will create preview deployments
-
Custom domain (optional):
- Add a custom domain in the Cloudflare Pages settings
Contributing
Section titled “Contributing”- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
License
Section titled “License”This project is licensed under the MIT License - see the LICENSE file for details.