Astro website with React and Tailwindcss.
Tags: astro, react, tailwindcss
Published: 2024-02-01 04:19:13

## What is Astro? 
Astro is the web framework designed for a content-rich website, with minimum JavaScript sent to the client. By default, zero JavaScript is sent.

- Server-first approach (Not SPA, but MPA) for SEO and fast speed
- Easy to use like HTML　for any developers
- For dynamic UI, you can use any framework such as React, Vue, Svelte, etc.

If you're thinking of making a **website that only requires a minimum amount of JavaScript** such as a Landing Page, Blog or Company page, Astro would be the best option!

I used Astro to make a landing page this time.


## Let's get started

### Installation

* Install astro

```
npm create astro@latest {directory name}
```

While you're installing, answer the questions like this.

![Question Astro asking](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q22bfccifs1s96x4g99i.png)


* Install React
```
npx astro add react
```

* Install Tailwindcss 
```
npx astro add tailwind
```

### Tailwind Css Setup

Delete unnecessary code in Layout.astro and index.astro.

* Font

Add google font inside `<head>` in Layout.astro file.

```javascript

		<link rel="preconnect" href="https://fonts.googleapis.com">
		<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
		<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">

```

Change tailwind.condig.mjs file

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
  theme: {
    extend: {
      fontFamily: {
        body: ['"Roboto", sans-serif'],
      },
    },
  },
  plugins: [],
};

```

* Theme

Add to tailwind config　for theme colours, fontSize or anything you want.

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'],
  theme: {
    extend: {
      fontFamily: {
        body: ['"Roboto", sans-serif'],
      },
      // Any theme you want to add here
      colors: {
        black: '#000000',
        white: '#FFFFFF',
        brand: '#4FD1C5',
        main: {
          50: '#F1FAFA',
          100: '#E3F5F4',
          200: '#B9E7E3',
          300: '#8FD9D1',
          400: '#4B9D96',
          500: '#37928A',
          600: '#2C756E',
          700: '#276661',
          800: '#215853',
          900: '#1C4945',
        },
      }
    },
  },
  plugins: [],
};

```

Now we've done the minimum settings and are ready to make a website!

## How to use it

After you finish setting, you see the default directory like this.

![Actual src directory with src/components, src/layouts, src/pages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwi6i6hfl9u3qlp3kgle.png)

So we can follow this directory structure.

In this case, I made a landing page with only one page, so the page file looks like this.

```javascript
---
import Layout from '../layouts/Layout.astro';
import Hero from '../components/Hero.astro';
import Problem from '../components/Problem.astro';
import Solution from '../components/Solution.astro';
import Feature from '../components/Feature.astro';
import Comparison from '../components/Comparison.astro';
import Flow from '../components/Flow.astro';
import Faq from '../components/Faq.astro';
import Message from '../components/Message.astro';
import Cta from '../components/Cta.astro';
import Footer from '../components/Footer.astro';
---

<Layout>
  <main class='pt-[58px] lg:pt-[72px]'>
    <Hero />
    <Problem />
    <Solution />
    <Feature />
    <Comparison />
    <Flow />
    <Faq />
    <Message />
    <Cta />
    <Footer />
  </main>
</Layout>
```

### Component Script & Props

Let's make components. 

Astro components are made up of two separate parts.

```javascript
---
// Component Script (JavaScript)
---
<!-- Component Template (HTML + JS Expressions) -->
```

* In the Component Script using a code fence (---), you can import other files, define variables or fetch API.

* In the Component Template, you can use JavaScript expressions similar to JSX.

```javascript

---
const points = [
  { text: 'No intermediary fees at all!' },
  { text: 'Unlimited scouts' },
  { text: 'Only ¥50,000 per month (completely free until the end of March 2023)'
---

<ul class="flex flex-col gap-2.5 relative z-20 lg:flex-row lg:gap-14">
  {points.map((point) => (
    <li>{point.text}</li>
  ))}
</ul>;
```

You can also pass props through components like React.

```javascript
---
type Props = {
  title: string;
  body: string;
  href: string;
}

const { href, title, body } = Astro.props;
---

<li class="link-card">
  <a href={href}>
    <h2>
      {title}
        <span>&rarr;</span>
    </h2>
    <p>
      {body}
    </p>
  </a>
</li>
```

```javascript
---
import Card from './Card.astro';
---

<Card title="Astro is awesome" body="This is..." href="https://www.google.com" />
```

### Use with React

You can make dynamic UI parts like Accordion with React.

```typescript
import { useState } from "react";
import { ChevronDown, ChevronUp } from "./icons";

type AccordionProps = {
  title: string;
  description: string;
};

export const Accordion = ({ title, description }: AccordionProps) => {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <div className="bg-white px-4 md:px-8 rounded-md">
      <button
        className="py-3 flex items-center text-sm text-left w-full md:py-4 md:text-md"
        onClick={() => setIsOpen((prev) => !prev)}
      >
        <span className="text-main-800 font-bold pr-2 text-xl shrink-0 leading-none self-start md:text-2xl md:pr-4">
          Q
        </span>
        {title}
        <span className="h-6 w-6 flex-shrink-0 pl-2 md:h-7 md:w-7 ml-auto">
          {isOpen ? <ChevronUp /> : <ChevronDown />}
        </span>
      </button>
      <div
        className={`flex flex-start font-normal text-sm  border-t border-gray-200 text-left md:text-md overflow-hidden transition-all ${
          isOpen ? "h-auto py-4 md:py-5" : "h-0 py-0"
        }`}
      >
        <span className="text-main-400 font-bold pr-2 text-xl shrink-0 leading-none md:text-2xl md:pr-4">
          A
        </span>
        <p>{description}</p>
      </div>
    </div>
  );
};

```

Then you import it in astro component.

```javascript
---
import { Accordion } from './Accordion';

const faqs = [
  {
    title:
      'Question1',
    description:
      'Answer1',
  },
  {
    title:
      'Question2',
    description:
       'Answer2',
  },
];
---

<section id='faq' class='bg-gray-100'>
  <div class='section-container'>
    <div class='font-bold text-center'>
      <h2 class='text-2xl md:text-4xl'>FAQ</h2>

      <div class='flex flex-col gap-4 mt-8'>
        {
          faqs.map((faq) => (
            <Accordion
              title={faq.title}
              description={faq.description}
              client:visible
            />
          ))
        }
      </div>
    </div>
  </div>
</section>

```

The cool part is you can pass props the same as React from Astro file:flushed:

You can use JS frameworks like this because of Astro Island.

Astro Island is dynamic UI components on the page. Astro can combine static HTML and interactive UI components in one page. 

![Showing parts structure with island and static HTML](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rtmg35o4es0c1yt5nvr0.png)

[Official site](https://docs.astro.build/en/concepts/islands/#what-is-an-island
)

With interactive islands, you need to mark using client:* directives to control how the UI components are hydrated. I used `client:visible` to display the Accordion component. It means that priority is low, and this component is rendered when it is in the viewport because the accordion is not necessary to render in the first view. 

You can refer to all the directives from here: [Client Directives](https://docs.astro.build/en/reference/directives-reference/#client-directives)


### Image

You can use the image optimization feature provided by Astro.
However, it is a bit different from when I used it last year, so I won't explain it in detail.

The official website says the image will be optimized if you do the following.

- Images are kept in src/ directory
- Images must be imported into the file
- Use Astro’s built-in <Image /> component

```javascript
---
import { Image } from 'astro:assets';
import localBirdImage from '../../images/subfolder/localBirdImage.png';
---
<Image src={localBirdImage} alt="A bird sitting on a nest of eggs."/>
```

### Astro Icon

For icons, [astro-icon](https://www.astroicon.dev/) is definitely helpful.

Super easy. You store svg file in `/src/icons/` and use astro-icon.

```
npx astro add astro-icon
```

```javascript
---
import { Icon } from 'astro-icon/components'
---

<!-- Embed the contents of the `/src/icons/logo.svg` file -->
<Icon name="logo" />

<!-- Embed the contents of the `/src/icons/logos/astro.svg` file -->
<Icon name="logos/astro" />
```

You can also use open source icon set.

```
npm install @iconify-json/mdi
```

```javascript
---
import { Icon } from 'astro-icon/components'
---

<!-- Embed the `account` icon from `@iconify-json/mdi` -->
<Icon name="mdi:account" />
```

## Conclusion
Astro not only offers a seamless user experience but also intuitively understands the needs of web developers. It is truly an amazing framework for creating websites!