Skip to main content

Vite Frontend

Overview

The application uses Vite for modern JavaScript/TypeScript builds with React.

  • Build Tool: Vite 5.4 with vite-plugin-ruby
  • Framework: React 16.13 with TypeScript
  • Directory: app/frontend/

Directory Structure

app/frontend/
├── entrypoints/
│ └── application.tsx # Main entry point, mounts DonationsApp
├── components/
│ ├── DonationsApp.tsx # Main React application
│ └── donations/ # Donation-specific components
├── api/ # Generated route helpers (js_from_routes)
├── lib/ # Shared utilities
└── globals.d.ts # TypeScript declarations

Development

Running the Development Server

# Start all services (Rails, Vite, Sidekiq)
foreman start -f Procfile.dev

# Or start Vite separately
bin/vite dev

Building Assets

# Development build
RAILS_ENV=development rails assets:precompile

# Production build
RAILS_ENV=production rails assets:precompile

Linting

yarn lint        # Lint JavaScript/TypeScript (Biome)
yarn typecheck # TypeScript type checking

React App Mounting

The DonationsApp is mounted directly in app/frontend/entrypoints/application.tsx:

import React from 'react'
import ReactDOM from 'react-dom'
import DonationsApp from '../components/DonationsApp'

function mountDonationsApp() {
const container = document.getElementById('donations-app')
const propsScript = document.getElementById('donations-app-props')

if (container && propsScript) {
const props = JSON.parse(propsScript.textContent || '{}')
ReactDOM.render(<DonationsApp {...props} />, container)
}
}

Passing Props from Rails

In the view (app/views/donations/home/index.html.erb):

<div id="donations-app"></div>
<script type="application/json" id="donations-app-props">
<%= {
stripeKey: ENV.fetch('STRIPE_PUBLISHABLE_KEY'),
recaptchaSiteKey: Recaptcha.configuration.site_key!,
gtmContainerId: donations_gtm_container_id
}.to_json.html_safe %>
</script>

Route Helpers

Rails routes are exposed to JavaScript via js_from_routes. Generated helpers are in app/frontend/api/.

import { donationsStripe } from '../api'

// Usage
api.post(donationsStripe.checkout.path(), params)
api.post(donationsStripe.paymentIntent.path(), params)

To regenerate after route changes:

bin/rails js_from_routes:generate

Configuration Files

FilePurpose
vite.config.mtsVite configuration
config/vite.jsonVite Ruby settings
tsconfig.jsonTypeScript configuration
biome.jsonLinter configuration

Layout Integration

The donations layout (app/views/layouts/donations.html.erb) includes:

<%= vite_react_refresh_tag %>
<%= vite_client_tag %>
<%= vite_javascript_tag 'entrypoints/application.tsx' %>

Troubleshooting

Vite Dev Server Not Starting

  • Check that port 3036 is available
  • Ensure Node.js 20.x is installed

Assets Not Loading

  • Make sure bin/vite dev is running in development
  • Check that assets were precompiled for production

React App Not Mounting

  • Verify #donations-app div exists in the view
  • Check browser console for JavaScript errors
  • Ensure props JSON is valid

Dependencies

Core

  • vite (^5.4.0)
  • vite-plugin-ruby (^5.1.1)
  • @vitejs/plugin-react (^4.7.0)
  • react / react-dom (^16.13.0)

Development

  • typescript (^4.9.5)
  • @biomejs/biome (linting)