Creating New Components
Relevant source files
The following files were used as context for generating this wiki page:This document outlines the architecture, patterns, and best practices for creating new React components within the grapevine_website project. The project utilizes a component-based architecture built on Next.js, TypeScript, and Tailwind CSS to ensure a modular, maintainable, and scalable frontend.
Components are the fundamental building blocks of the user interface. They are organized into two main categories: primitive components, which are basic, reusable UI elements, and composite components, which are larger, feature-specific blocks composed of primitives and other components. This structure promotes consistency and accelerates development.
Component Architecture
The project's component architecture is organized within the components/ directory. This directory contains subdirectories for different types of components, such as primitives, animations, and icons, as well as higher-level composite components.
Sources: README.md
Directory Structure
The following diagram illustrates the component directory structure.
This diagram shows the main directories, with a focus on how pages in app/ consume components from the components/ directory.
Sources: README.md
Core Technologies
Components are built using a specific set of technologies to ensure consistency in styling, functionality, and type safety.
| Library | Version | Purpose |
|---|---|---|
next | 16.0.3 | React framework for server-side rendering and routing. |
react | 19.2.0 | Core library for building user interfaces. |
typescript | 5 | Language for static typing of props and state. |
tailwindcss | 4 | Utility-first CSS framework for styling. |
framer-motion | 12.23.24 | Animation library for rich user interactions. |
lucide-react | 0.554.0 | Library for SVG icons. |
clsx | 2.1.1 | Utility for constructing className strings conditionally. |
tailwind-merge | 3.4.0 | Utility to merge Tailwind CSS classes without style conflicts. |
Sources: package.json:14-21, README.md:6-10
Component Types
The codebase distinguishes between low-level primitive components and high-level composite components.
Primitive Components
Located in components/primitives/, these are the most basic, reusable UI elements. They are unopinionated and serve as the foundation for more complex components.
Examples of primitive components used throughout the application include:
ButtonInputSelectTextareaSectionContainer
The ContactForm component is a clear example of how these primitives are composed to build a functional UI feature.
Sources: components/contact-form.tsx:10, README.md:32
Composite Components
These are larger, feature-specific components located in the root of the components/ directory. They are built by composing primitive components and other composite components. They often contain business logic, state management, and are directly consumed by pages.
Examples include:
ContactForm(components/contact-form.tsx)Features(components/features.tsx)MarketplaceOverview(components/marketplace-overview.tsx)Footer(components/footer.tsx)
The following diagram shows how the Product page is constructed from multiple composite components.
This demonstrates the modularity of the page structure, where each major section is a self-contained component.
Sources: app/product/page.tsx:2-7
Creating a New Component
Follow these steps to create a new component that aligns with the project's existing patterns.
1. Define Props with TypeScript
All components must have strongly typed props using a TypeScript interface. This ensures clarity and prevents runtime errors.
// components/marketplace-overview.tsx:24-30
export interface MarketplaceOverviewProps {
title: React.ReactNode;
subtitle?: string;
centerIcon: IconName;
surroundingIcons: IconName[];
mainTitle: string;
features: Feature[];
// ... other props
}
Sources: components/marketplace-overview.tsx:24-30
2. Manage State and Interactivity
Use React hooks like useState and useEffect for managing component state. For complex state logic, consider patterns seen in ContactForm.
The diagram below illustrates the state flow for the contact form submission.
This sequence shows how user actions trigger state changes, validation, an API call, and UI updates.
Sources: components/contact-form.tsx:43-120
3. Implement Animations
Use the framer-motion library for animations. The project includes a reusable AnimateOnScroll component, and many components use motion.div for more granular control over animations like hover effects, scaling, and rotation.
// components/marketplace-overview.tsx:103-114
<motion.div
className="bg-primary absolute inset-[20%] flex items-center justify-center rounded-full shadow-lg"
initial={{ scale: 0, opacity: 0 }}
whileInView={{ scale: 1, opacity: 1 }}
viewport={{ once: true, margin: "-100px" }}
transition={{
duration: 0.4,
delay: 0.1,
ease: "easeOut",
}}
>
This snippet from MarketplaceOverview shows an element animating into view with a scale and opacity transition when it enters the viewport.
Sources: components/marketplace-overview.tsx:103-114, components/contact-form.tsx:6
4. Use Icons
The project uses lucide-react via a custom Icon component that supports dynamic icon loading. This standardizes icon usage across the application.
Usage:
import Icon from "@/components/icon";
<Icon name="check" className="text-primary" size={24} />
Available Props:
| Prop | Type | Description |
|---|---|---|
name | IconName | Icon name from Lucide (required) |
size | number | Icon size in pixels |
className | string | Tailwind/CSS classes |
strokeWidth | number | Stroke width (default: 2) |
Sources: README.md:36-58, components/features.tsx:11
5. Style with Tailwind CSS
Use Tailwind CSS utility classes for all styling. For conditional classes, use the cn utility function from lib/utils, which combines clsx and tailwind-merge.
// components/features.tsx:215-221
<div
className={cn(
"flex cursor-pointer flex-col border-l-4 pr-5 transition-all",
isActive
? ["border-l-primary rounded-r-xl bg-white py-4 pl-5 opacity-100"]
: ["border-l-transparent bg-transparent py-0 pl-0 opacity-50"]
)}
onClick={() => handleFeatureClick(index)}
>
This example shows how to apply different styles based on the isActive state.
Sources: components/features.tsx:215-221, lib/utils.ts, package.json:14,21
Summary
Creating new components in this project involves adhering to a well-defined structure and a consistent set of technologies. By leveraging primitive components, defining clear props with TypeScript, and using established patterns for state management, styling, and animations, developers can build new features efficiently while maintaining code quality and a cohesive user experience. The composite nature of components allows pages to be assembled declaratively, making the overall application structure easy to understand and extend.
Sources: README.md, app/product/page.tsx