DEV Community

Kana
Kana

Posted on • Edited on

Astro website with React and Tailwindcss.

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}
Enter fullscreen mode Exit fullscreen mode

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

Question Astro asking

  • Install React
npx astro add react
Enter fullscreen mode Exit fullscreen mode
  • Install Tailwindcss
npx astro add tailwind
Enter fullscreen mode Exit fullscreen mode

Tailwind Css Setup

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

  • Font

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


        <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">

Enter fullscreen mode Exit fullscreen mode

Change tailwind.condig.mjs file

/** @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: [],
};

Enter fullscreen mode Exit fullscreen mode
  • Theme

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

/** @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: [],
};

Enter fullscreen mode Exit fullscreen mode

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

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.

---
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>
Enter fullscreen mode Exit fullscreen mode

Component Script & Props

Let's make components.

Astro components are made up of two separate parts.

---
// Component Script (JavaScript)
---
<!-- Component Template (HTML + JS Expressions) -->
Enter fullscreen mode Exit fullscreen mode
  • 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.


---
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>;
Enter fullscreen mode Exit fullscreen mode

You can also pass props through components like React.

---
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>
Enter fullscreen mode Exit fullscreen mode
---
import Card from './Card.astro';
---

<Card title="Astro is awesome" body="This is..." href="https://www.google.com" />
Enter fullscreen mode Exit fullscreen mode

Use with React

You can make dynamic UI parts like Accordion with React.

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>
  );
};

Enter fullscreen mode Exit fullscreen mode

Then you import it in astro component.

---
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>

Enter fullscreen mode Exit fullscreen mode

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

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

Official site

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

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 component
---
import { Image } from 'astro:assets';
import localBirdImage from '../../images/subfolder/localBirdImage.png';
---
<Image src={localBirdImage} alt="A bird sitting on a nest of eggs."/>
Enter fullscreen mode Exit fullscreen mode

Astro Icon

For icons, astro-icon is definitely helpful.

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

npx astro add astro-icon
Enter fullscreen mode Exit fullscreen mode
---
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" />
Enter fullscreen mode Exit fullscreen mode

You can also use open source icon set.

npm install @iconify-json/mdi
Enter fullscreen mode Exit fullscreen mode
---
import { Icon } from 'astro-icon/components'
---

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

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!

Top comments (0)