helix_kit Svelte Themes

Helix_kit

Core template for full-featured Svelte/Rails/Inertia apps

Svelte App Kit for Ruby on Rails

Helix Kit Logo

This is a start app kit template analogous to Jumpstart Pro or BulletTrain, but using Svelte and Inertia.js for the frontend, with Ruby on Rails as the backend, and including a number of other useful libraries and tools.

Features

  • Svelte 5 - A modern JavaScript framework for building user interfaces.
  • Ruby on Rails - A powerful web application framework for building server-side applications.
  • Inertia.js Rails - Enables single-page applications using classic Rails routing and controllers.
  • ShadcnUI - A collection of UI components for Svelte.
  • Tailwind CSS - A utility-first CSS framework for building custom designs.
  • Phosphor Icons - A versatile icon library for user interfaces.
  • JS Routes - A library for generating JavaScript routes in Rails applications.
  • Rails Authentication - Built-in authentication using the default Rails 8 authentication system.
  • Vite - A fast and modern frontend bundler.
  • PostgreSQL - A powerful, open-source relational database system.
  • DaisyUI - A plugin for Tailwind CSS that provides a set of pre-designed components, for rapid prototyping of components not covered by ShadcnUI.
  • Claude Code Ready - Clear documentation in /docs/ to enable Claude Code to perform at its best.
  • SolidQueue/Cable/Cache - Set up in development environment, for background jobs, real-time features, and caching.
  • Obfuscated IDs - For better security and aesthetics in URLs. Copy implementation from BulletTrain.
  • Testing - Full test suite setup with Playwright Component Testing for page testing, Vitest for Svelte component unit testing, Minitest for Rails model and controller testing.
  • Full-featured user system - Necessary for most commercial applications, but not included in the default user setup.
    • User signup and confirmation
    • Personal/Organization Accounts
    • Site Admin
    • User Profiles
    • Invitations
    • Roles
  • Svelte Object Synchronization - Using ActionCable and Inertia's partial reload and a custom Regitry to keep Svelte $props up to date in real-time.
  • Audit Logging with audit log viewer (required in many business applications).

Target features (TODO)

  • **Discard gem: Never delete anything important (e.g. accounts, users, etc), only discard it.
  • AI Integration features:
    • OpenRouter integration
    • Prompt system
    • Basic Conversation System
    • Agentic Conversation System
  • MultiAttachment system supporting:
    • Direct uploads to S3
    • PDF/Document parsing
    • URL fetch
    • Free text
  • Organisation account settings:
    • Logo
    • Company Name
  • All account settings:
    • Billing
  • API capability:
    • API key management
    • API key usage tracking
    • API key rate limiting
    • API key billing
    • API key audit logging
    • API documentation

Explicitly out of scope

  • Internationalization (i18n)

Installation

  1. Click "Use this template" to create a new repository from this template.
  2. Clone your new repository:
    git clone https://github.com/<youruser>/<your_repo>
    cd <your-repo>
    
  3. Install dependencies:
    bundle install
    npm install
    
  4. Setup the database:
    rails db:create:all
    rails db:setup db:prepare
    rails db:migrate:cache db:migrate:queue db:migrate:cable
    rails db:schema:dump:cable db:schema:dump:cache db:schema:dump:queue
    
    Check that the solid* databases have been created by checking db/cable_schema.rb, db/cache_schema.rb, and db/queue_schema.rb and seeing that they contain a comment at the top about auto-generation.
  5. Either download the config/master.key from a colleague, or rails credentials:edit and add the following credentials:
    aws:
      access_key_id: ...
      s3_bucket: ...
      s3_region: ...
      secret_access_key: ...
    
    ai:
      claude:
        api_token: ...
      open_ai:
        api_token: ...
      openrouter:
        api_token: ...
    
    honeybadger:
      api_key: ...
    
  6. Start the development server:
    bin/dev
    
  7. Open in browser at localhost:3100

Optional: Claude setup

Necessary for Claude Code to be full featured.

claude mcp add --scope=local playwright npx @executeautomation/playwright-mcp-server
claude mcp add --scope=local snap-happy npx @mariozechner/snap-happy

Usage

This template integrates Svelte with Rails using Inertia.js to manage front-end routing while keeping Rails' backend structure. It uses Vite for asset bundling, and all frontend code is located in the app/frontend directory. Place assets such as images and fonts inside the app/frontend/assets folder.

Contributing

Feel free to fork this repository and submit pull requests with improvements, fixes, or additional features.

Documentation

Real-time Synchronization System

This application includes a powerful real-time synchronization system that automatically updates Svelte components when Rails models change, using ActionCable and Inertia.js partial reloads.

How It Works

  1. Rails models broadcast minimal "marker" messages when they change
  2. Svelte components subscribe to these broadcasts via ActionCable
  3. When a broadcast is received, Inertia performs a partial reload of just the affected props
  4. Updates are debounced (300ms) to handle multiple rapid changes efficiently

Key Files

Rails Side:

JavaScript/Svelte Side:

Usage Example

1. Add to your Rails model:

class Account < ApplicationRecord
  include SyncAuthorizable
  include Broadcastable
  
  # Configure what to broadcast to
  broadcasts_to :all  # Broadcast to admin collection (for index pages)
end

class AccountUser < ApplicationRecord
  include Broadcastable
  
  belongs_to :account
  belongs_to :user
  
  # Broadcast changes to the parent account
  broadcasts_to :account
end

class User < ApplicationRecord
  include Broadcastable
  
  has_many :accounts
  
  # Broadcast changes to all associated accounts (uses Rails reflection)
  broadcasts_to :accounts
end

Understanding broadcasts_to:

  • :all - Broadcasts to a collection channel (typically for admin index pages)
  • Association name - Broadcasts to associated records automatically:
    • For belongs_to/has_one: Broadcasts to the single associated record
    • For has_many/has_and_belongs_to_many: Broadcasts to each record in the collection
  • Rails uses reflection to automatically detect the association type and handle it correctly

2. Use in your Svelte component:

For static subscriptions:

<script>
  import { useSync } from '$lib/use-sync';
  
  let { accounts = [] } = $props();
  
  // Simple static subscriptions
  useSync({
    'Account:all': 'accounts',  // Updates when any account changes
  });
</script>

For dynamic subscriptions (when the subscribed objects can change):

<script>
  import { createDynamicSync } from '$lib/use-sync';
  
  let { accounts = [], selected_account = null } = $props();
  
  // Create dynamic sync handler
  const updateSync = createDynamicSync();
  
  // Update subscriptions when selected_account changes
  $effect(() => {
    const subs = { 'Account:all': 'accounts' };
    if (selected_account) {
      subs[`Account:${selected_account.id}`] = 'selected_account';
    }
    updateSync(subs);
  });
</script>

That's it! Your component will now automatically update when the data changes on the server.

Complete Example: Controller + Model + Component

Here's how all the pieces work together:

Rails Controller:

# app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
  def index
    render inertia: "Dashboard", props: {
      current_user: current_user.as_json,
      account: current_account.as_json,
      notifications: current_user.notifications
    }
  end
end

Rails Models:

# app/models/notification.rb
class Notification < ApplicationRecord
  include Broadcastable
  belongs_to :user
  
  # Broadcast changes to the parent user
  broadcasts_to :user
end

# app/models/user.rb  
class User < ApplicationRecord
  include Broadcastable
  has_many :accounts, through: :account_users
  
  # Broadcast changes to all associated accounts
  broadcasts_to :accounts
end

Svelte Component:

<script>
  import { useSync } from '$lib/use-sync';
  
  // These prop names match what the controller sends
  let { current_user, account, notifications } = $props();
  
  // Subscribe to updates - map channels to props to reload
  useSync({
    [`User:${current_user.id}`]: 'current_user',
    [`Account:${account.id}`]: 'account',
    [`Notification:all`]: 'notifications'
  });
</script>

The key insight: The model just broadcasts its identity (e.g., "User:123"), and the Svelte component decides which props need reloading based on its subscriptions.

Authorization Model

  • Objects with an account property: Accessible by all users in that account
  • Objects without an account property: Admin-only access
  • Site admins can subscribe to :all collections for any model

Testing

Run the synchronization tests:

rails test test/channels/sync_channel_test.rb
rails test test/models/concerns/broadcastable_test.rb

See the in-app documentation for more detailed information and advanced usage.

JSON Serialization with json_attributes

This application includes a powerful convention for controlling how Rails models are serialized to JSON, with automatic ID obfuscation for better security and cleaner URLs.

How It Works

The json_attributes concern provides a declarative way to specify which attributes and methods should be included when a model is converted to JSON (for Inertia props or API responses). It also automatically obfuscates model IDs using to_param.

Key Features

  1. Declarative Attribute Selection - Explicitly define which attributes/methods to include
  2. Automatic ID Obfuscation - IDs are automatically replaced with obfuscated versions via to_param
  3. Boolean Key Cleaning - Methods ending with ? have the ? removed in JSON (e.g., admin? becomes admin)
  4. Association Support - Include associated models with their own json_attributes
  5. Context Propagation - Pass context (like current_user) through nested associations

Usage Example

class User < ApplicationRecord
  include JsonAttributes
  
  # Specify what to include in JSON, excluding sensitive fields
  json_attributes :full_name, :site_admin, except: [:password_digest]
end

class Account < ApplicationRecord
  include JsonAttributes
  
  # Include boolean methods (the ? will be stripped in JSON)
  json_attributes :personal?, :team?, :active?, :is_site_admin, :name
end

class AccountUser < ApplicationRecord
  include JsonAttributes
  
  # Include associations with their json_attributes
  json_attributes :role, :confirmed_at, include: { user: {}, account: {} }
end

In Controllers

class AccountsController < ApplicationController
  def show
    @account = current_user.accounts.find(params[:id])
    
    render inertia: "accounts/show", props: {
      # as_json automatically uses json_attributes configuration
      account: @account.as_json,
      # Pass current_user context for authorization in nested associations
      members: @account.account_users.as_json(current_user: current_user)
    }
  end
end

Benefits

  • Security: Sensitive attributes like password_digest are never accidentally exposed
  • Clean URLs: Obfuscated IDs provide better aesthetics and security
  • Consistency: All models serialize the same way throughout the application
  • Performance: Only specified attributes are serialized, reducing payload size
  • Maintainability: JSON structure is defined in one place (the model)

See the in-app documentation for more detailed information and advanced usage.

License

This project is open-source and available under the MIT License.

Top categories

Loading Svelte Themes