Back to catalog

astro-component-generator

Generate Astro components with TypeScript frontmatter, TailwindCSS patterns, and i18n support

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: astro-component-generator
description: Generate Astro components with TypeScript frontmatter, TailwindCSS patterns, and i18n support
activation:
  keywords:
    - astro component
    - create astro
    - new component
    - generate astro
  triggers:
    - "\.astro
quot; auto_load_project: cv-astro --- # Astro Component Generator ## What This Is Generate production-ready Astro components with: - TypeScript frontmatter with Props interface - TailwindCSS `@apply` patterns - Scoped `<style>` blocks - i18n integration with `t()` helper - Proper file naming (PascalCase.astro) **For**: CV_Astro project (Astro 5.5.2 + React 18 + i18n) --- ## When to Activate Auto-activates when keywords detected: - "create astro component" - "new astro component" - "generate ProfileCard component" Manual activation: Use `/astro-component` command --- ## Component Types ### 1. **Simple Astro Component** (Static content) ```astro --- // src/components/ProfileCard.astro interface Props { name: string; title: string; image: string; bio?: string; } const { name, title, image, bio } = Astro.props; --- <article class="profile-card"> <img src={image} alt={name} class="profile-image" /> <div class="content"> <h2>{name}</h2> <p class="title">{title}</p> {bio && <p class="bio">{bio}</p>} </div> </article> <style> .profile-card { @apply bg-white rounded-lg shadow-md p-6 mb-4 hover:shadow-lg transition-shadow; } .profile-image { @apply w-24 h-24 rounded-full object-cover mb-4; } .content h2 { @apply text-2xl font-bold text-gray-900 mb-2; } .title { @apply text-lg text-gray-600 mb-3; } .bio { @apply text-gray-700 leading-relaxed; } </style> ``` --- ### 2. **Astro Component with i18n** ```astro --- // src/components/ExperienceCard.astro import { t, changeLanguage } from 'i18next'; interface Props { title: string; company: string; period: string; description: string; locale?: string; } const { title, company, period, description, locale = 'en' } = Astro.props; changeLanguage(locale); --- <article class="experience-card"> <div class="header"> <h3>{title}</h3> <span class="period">{period}</span> </div> <p class="company">{company}</p> <p class="description">{description}</p> <a href="#" class="read-more">{t('common:actions.readMore')}</a> </article> <style> .experience-card { @apply bg-white rounded-lg shadow-md p-6 mb-4; } .header { @apply flex justify-between items-start mb-3; } .header h3 { @apply text-xl font-bold text-gray-900; } .period { @apply text-sm text-gray-500; } .company { @apply text-lg text-gray-700 mb-3; } .description { @apply text-gray-600 leading-relaxed mb-4; } .read-more { @apply text-blue-600 hover:text-blue-800 font-medium; } </style> ``` --- ### 3. **Layout Component** ```astro --- // src/layouts/BaseLayout.astro import { t, changeLanguage } from 'i18next'; interface Props { title: string; description?: string; lang?: 'en' | 'es' | 'fr'; } const { title, description, lang = 'en' } = Astro.props; changeLanguage(lang); const siteTitle = `${title} | Oriol Macías`; --- <!doctype html> <html lang={lang}> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>{siteTitle}</title> {description && <meta name="description" content={description} />} <!-- SEO --> <link rel="canonical" href={Astro.url.href} /> <meta property="og:title" content={siteTitle} /> {description && <meta property="og:description" content={description} />} <slot name="head" /> </head> <body> <slot /> </body> </html> <style is:global> body { @apply bg-gray-50 text-gray-900 font-sans; } </style> ``` --- ## Generation Workflow ### Step 1: Analyze Requirements **Questions to ask user (if unclear)**: - Component type? (component, layout, page) - With i18n? (yes/no) - Styling approach? (tailwind, scoped, none) - Props needed? (list interfaces) ### Step 2: Generate File Path **Pattern**: `src/components/{ComponentName}.astro` - **Components**: `src/components/` - **Layouts**: `src/layouts/` - **Pages**: `src/pages/` **Naming**: PascalCase (e.g., `ProfileCard.astro`) ### Step 3: Generate TypeScript Props ```typescript interface Props { // Required props (no ?) title: string; content: string; // Optional props (with ?) className?: string; locale?: 'en' | 'es' | 'fr'; } ``` ### Step 4: Generate Frontmatter **Without i18n**: ```astro --- interface Props { title: string; } const { title } = Astro.props; --- ``` **With i18n**: ```astro --- import { t, changeLanguage } from 'i18next'; interface Props { title: string; locale?: string; } const { title, locale = 'en' } = Astro.props; changeLanguage(locale); --- ``` ### Step 5: Generate Template **Use semantic HTML**: ```astro <article class="component-name"> <h2>{title}</h2> <p>{content}</p> </article> ``` ### Step 6: Generate Styles **TailwindCSS with @apply**: ```astro <style> .component-name { @apply bg-white rounded-lg shadow-md p-6; } .component-name h2 { @apply text-2xl font-bold text-gray-900 mb-4; } </style> ``` **Scoped CSS (if custom)**: ```astro <style> .component-name { background: white; border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); padding: 1.5rem; } </style> ``` --- ## Best Practices ### ✅ DO 1. **Use TypeScript Props interface**: ```astro --- interface Props { title: string; count?: number; } --- ``` 2. **Destructure props**: ```astro const { title, count = 0 } = Astro.props; ``` 3. **Use TailwindCSS @apply for reusable patterns**: ```css .card { @apply bg-white rounded-lg shadow-md p-6; } ``` 4. **Include i18n for user-facing text**: ```astro <button>{t('common:actions.submit')}</button> ``` 5. **Use semantic HTML**: ```astro <article>, <section>, <nav>, <header>, <footer> ``` ### ❌ DON'T 1. **Don't skip Props interface**: ```astro <!-- ❌ Bad --> const { title } = Astro.props; <!-- ✅ Good --> interface Props { title: string; } const { title } = Astro.props; ``` 2. **Don't inline all TailwindCSS classes**: ```astro <!-- ❌ Bad (too many classes) --> <div class="bg-white rounded-lg shadow-md p-6 mb-4 hover:shadow-lg transition-shadow duration-300"> <!-- ✅ Good (use @apply in style block) --> <div class="card"> <style> .card { @apply bg-white rounded-lg shadow-md p-6 mb-4 hover:shadow-lg transition-shadow; } </style> ``` 3. **Don't hardcode text (use i18n)**: ```astro <!-- ❌ Bad --> <button>Submit</button> <!-- ✅ Good --> <button>{t('common:actions.submit')}</button> ``` 4. **Don't forget locale prop for i18n components**: ```astro <!-- ✅ Good --> interface Props { locale?: 'en' | 'es' | 'fr'; } ``` --- ## Validation After generation, check: - [ ] File created in correct directory - [ ] PascalCase naming (ComponentName.astro) - [ ] TypeScript Props interface defined - [ ] Props destructured - [ ] i18n integrated (if user-facing text) - [ ] TailwindCSS @apply used - [ ] Scoped styles added - [ ] Semantic HTML used - [ ] No syntax errors --- ## Example Usage **User Request**: ``` "Create a ProjectCard component with title, description, image, and tags" ``` **Generated Component**: ```astro --- // src/components/ProjectCard.astro interface Props { title: string; description: string; image: string; tags: string[]; link?: string; } const { title, description, image, tags, link } = Astro.props; --- <article class="project-card"> <img src={image} alt={title} class="project-image" /> <div class="content"> <h3>{title}</h3> <p>{description}</p> <div class="tags"> {tags.map(tag => ( <span class="tag">{tag}</span> ))} </div> {link && ( <a href={link} class="view-project" target="_blank" rel="noopener"> View Project → </a> )} </div> </article> <style> .project-card { @apply bg-white rounded-lg shadow-md overflow-hidden hover:shadow-xl transition-shadow; } .project-image { @apply w-full h-48 object-cover; } .content { @apply p-6; } .content h3 { @apply text-2xl font-bold text-gray-900 mb-3; } .content p { @apply text-gray-600 leading-relaxed mb-4; } .tags { @apply flex flex-wrap gap-2 mb-4; } .tag { @apply bg-blue-100 text-blue-800 text-sm px-3 py-1 rounded-full; } .view-project { @apply inline-block text-blue-600 hover:text-blue-800 font-medium; } </style> ``` --- ## Quick Reference | Pattern | Code | |---------|------| | **Props interface** | `interface Props { title: string; }` | | **Destructure** | `const { title } = Astro.props;` | | **i18n import** | `import { t, changeLanguage } from 'i18next';` | | **Set locale** | `changeLanguage(locale);` | | **Translation** | `{t('namespace:key')}` | | **TailwindCSS** | `<style> .class { @apply ...; } </style>` | | **Optional prop** | `count?: number` | | **Default value** | `const { count = 0 } = Astro.props;` |