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
| File | Purpose |
|---|---|
vite.config.mts | Vite configuration |
config/vite.json | Vite Ruby settings |
tsconfig.json | TypeScript configuration |
biome.json | Linter 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 devis running in development - Check that assets were precompiled for production
React App Not Mounting
- Verify
#donations-appdiv 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)