Interactive React components with client-side state management
The Drakk3 Portfolio uses React 19 for components that require client-side interactivity. Currently, there is only one React component: the Contact form.
Client-side state management (form inputs, toggles, etc.)
User interactions that require JavaScript
Real-time updates or animations
Form validation and submission
Astro components are static by default, so React is brought in only where interactivity is needed. This is called partial hydration.
Astro allows you to mix multiple frameworks (React, Vue, Svelte, etc.) in the same project. This portfolio uses React exclusively for interactive components.
Location:src/components/Contact.tsxThe Contact form is the only React component in the portfolio. It handles form state, submission, and success feedback.
React components must be hydrated on the client using Astro’s client:* directives.
src/pages/index.astro
---import Layout from '../layouts/Layout.astro';import Navbar from '../components/Navbar.astro';import Hero from '../components/Hero.astro';import About from '../components/About.astro';import Skills from '../components/Skills.astro';import Projects from '../components/Projects.astro';import Contact from '../components/Contact';import Footer from '../components/Footer.astro';---<Layout> <Navbar /> <main> <Hero /> <About /> <Skills /> <Projects /> <Contact client:load /> </main> <Footer /></Layout>
client:load
client:visible
client:idle
client:only
Used in this project. Hydrates the component immediately on page load.
<Contact client:load />
When to use:
Component is visible on initial page load
Interactivity is needed immediately
Example: forms, navigation menus
Hydrates when the component enters the viewport.
<Contact client:visible />
When to use:
Component is below the fold
Want to defer hydration for performance
Example: carousels, modals
Hydrates when the browser is idle (after requestIdleCallback).
<Contact client:idle />
When to use:
Non-critical interactive components
Want to prioritize other page resources
Example: chat widgets, analytics
Skips server rendering entirely, only renders on the client.
<Contact client:only="react" />
When to use:
Component depends on browser APIs (window, document)
Can’t be server-rendered
Example: canvas animations, WebGL
Important: The Contact component is imported without the .tsx extension:
import Contact from '../components/Contact'; // ✅ Correctimport Contact from '../components/Contact.tsx'; // ❌ Wrong
startTransition: Wraps async operations without blocking UI
Button shows spinner while isPending is true
This is a demo implementation. In production, you’d replace the setTimeout with an actual API call (e.g., to Resend, SendGrid, or a serverless function).
import { useState } from 'react';export default function MyComponent() { const [count, setCount] = useState(0); return ( <button onClick={() => setCount(count + 1)}> Clicked {count} times </button> );}
2
Import in Astro
src/pages/index.astro
---import MyComponent from '../components/MyComponent';---<Layout> <MyComponent client:load /></Layout>
3
Choose hydration directive
client:load - Hydrate immediately
client:visible - Hydrate when visible
client:idle - Hydrate when browser is idle
client:only="react" - Client-only (no SSR)
Remember: Always add a client:* directive when using React components in Astro. Without it, the component will be rendered as static HTML with no interactivity.