Files
aperonight/docs/creating-shadcn-react-components.md
2025-08-28 13:43:05 +02:00

6.3 KiB

Creating New Shadcn and React Components

This guide explains how to create new Shadcn (UI) components and React components in this Rails application with React frontend.

Overview

This project uses:

  • Shadcn/ui for UI components (built on Radix UI and Tailwind CSS)
  • React for frontend components
  • Rails as the backend framework
  • esbuild for JavaScript bundling

Directory Structure

app/
├── javascript/
│   ├── components/
│   │   └── ui/           # Shadcn components
│   └── controllers/      # React controllers
├── views/
│   └── components/       # Rails view components
└── docs/               # Documentation

Creating Shadcn Components

1. Using the Shadcn CLI

The easiest way to add new Shadcn components is using the CLI:

# Navigate to the project root
cd /home/acid/Documents/aperonight

# Add a new component (example: adding a card)
npx shadcn-ui@latest add card

This will:

  • Install the component to app/javascript/components/ui/
  • Update the components.json configuration
  • Create the necessary TypeScript/JavaScript files

2. Manual Component Creation

If the CLI is not available, create components manually:

Create the component file

# Create a new component (example: button.jsx)
touch app/javascript/components/ui/button.jsx

Basic component structure

// app/javascript/components/ui/button.jsx
import * as React from "react"
import { cn } from "@/lib/utils"

const Button = React.forwardRef(({ className, ...props }, ref) => {
  return (
    <button
      className={cn(
        "inline-flex items-center justify-center rounded-md text-sm font-medium",
        className
      )}
      ref={ref}
      {...props}
    />
  )
})
Button.displayName = "Button"

export { Button }

Creating React Components

1. Controller-Based Components

For components that need Rails integration:

Create controller file

# Create a new controller
touch app/javascript/controllers/my_component_controller.js

Basic controller structure

// app/javascript/controllers/my_component_controller.js
import { Controller } from "@hotwired/stimulus"
import React from "react"
import ReactDOM from "react-dom/client"

export default class extends Controller {
  static targets = ["container"]

  connect() {
    const root = ReactDOM.createRoot(this.containerTarget)
    root.render(<MyComponent />)
  }
}

2. Standalone React Components

For reusable React components:

Create component file

# Create a new React component
touch app/javascript/components/MyNewComponent.jsx

Component structure

// app/javascript/components/MyNewComponent.jsx
import React from "react"

const MyNewComponent = ({ title, description }) => {
  return (
    <div className="p-4 border rounded-lg">
      <h2 className="text-lg font-semibold">{title}</h2>
      <p className="text-gray-600">{description}</p>
    </div>
  )
}

export default MyNewComponent

Integration Patterns

1. Using in Rails Views

To use components in Rails views:

Create partial

<!-- app/views/components/_my_component.html.erb -->
<div data-controller="my-component">
  <div data-my-component-target="container"></div>
</div>

Include in page

<!-- app/views/pages/home.html.erb -->
<%= render "components/my_component" %>

2. Direct React Rendering

For pages that are primarily React:

Create page component

// app/javascript/components/pages/HomePage.jsx
import React from "react"
import { Button } from "@/components/ui/button"

const HomePage = () => {
  return (
    <div className="container mx-auto">
      <h1>Welcome</h1>
      <Button>Get Started</Button>
    </div>
  )
}

export default HomePage

Configuration Updates

1. Update components.json

{
  "style": "default",
  "rsc": false,
  "tsx": false,
  "tailwind": {
    "config": "tailwind.config.js",
    "css": "app/assets/stylesheets/application.postcss.css",
    "baseColor": "slate",
    "cssVariables": true
  },
  "aliases": {
    "components": "app/javascript/components",
    "utils": "app/javascript/lib/utils"
  }
}

2. Update JavaScript entry point

// app/javascript/application.js
import "./components"
import "./controllers"

Naming Conventions

Shadcn Components

  • Use kebab-case for filenames: button.jsx, card.jsx
  • Use PascalCase for exports: export { Button }
  • Follow Radix UI naming patterns

React Components

  • Use PascalCase for filenames: MyComponent.jsx
  • Use PascalCase for components: const MyComponent = () => {}
  • Use camelCase for props: myProp, onClick

Testing Components

1. Create test file

# Create test file
touch test/components/my_component_test.rb

2. Write component test

// test/components/my_component_test.jsx
import { render, screen } from "@testing-library/react"
import MyComponent from "../../app/javascript/components/MyComponent"

test("renders component", () => {
  render(<MyComponent title="Test" />)
  expect(screen.getByText("Test")).toBeInTheDocument()
})

Common Patterns

1. Props Pattern

// Pass Rails data as props
const MyComponent = ({ user, config }) => {
  return <div>{user.name}</div>
}

2. Event Handling

// Handle events from Rails
const MyComponent = ({ onAction }) => {
  return <button onClick={onAction}>Click me</button>
}

3. Styling Integration

// Use Tailwind classes
const MyComponent = () => {
  return <div className="bg-white dark:bg-gray-800">Content</div>
}

Troubleshooting

Common Issues

  1. Component not rendering: Check controller connection
  2. Styling issues: Verify Tailwind classes
  3. Props not passing: Check data-controller attributes
  4. Import errors: Verify alias paths in components.json

Debug Steps

  1. Check browser console for errors
  2. Verify component file exists in correct location
  3. Check import paths in application.js
  4. Verify Rails view includes correct data attributes

Example created for testing purpose

<!-- Shadcn Button Test -->
<div data-controller="shadcn-test" class="mt-4">
    <div data-shadcn-test-target="container"></div>
</div>