Back to list
radioactive-labs

plutonium-forms

by radioactive-labs

Build production-ready Rails apps in minutes, not days. Convention-driven, fully customizable, AI-ready.

52🍴 7📅 Jan 23, 2026

SKILL.md


name: plutonium-forms description: Plutonium forms - custom templates, Phlex form components, field builders, and theming

Plutonium Forms

Plutonium forms are built on Phlexi::Form, providing a Ruby-first approach to form building with Phlex components.

Form Class Hierarchy

Phlexi::Form::Base
└── Plutonium::UI::Form::Base       # Base with Plutonium components
    ├── Plutonium::UI::Form::Resource   # Resource CRUD forms
    │   └── Plutonium::UI::Form::Interaction  # Interactive action forms
    └── Plutonium::UI::Form::Query      # Search/filter forms

Customizing Resource Forms

Override in Definition

class PostDefinition < ResourceDefinition
  class Form < Form
    def form_template
      # Your custom form layout
      render_fields
      render_actions
    end
  end
end

Form Template Methods

MethodDescription
form_templateMain template method to override
render_fieldsRender all permitted fields
render_resource_field(name)Render a single field by name
render_actionsRender submit buttons
fields_wrapper { }Wrapper div for field grid
actions_wrapper { }Wrapper div for buttons

Form Attributes

AttributeDescription
object / recordThe form object being edited
resource_fieldsArray of permitted field names
resource_definitionThe definition instance

Custom Form Layout

Sectioned Form

class PostDefinition < ResourceDefinition
  class Form < Form
    def form_template
      section("Basic Information") {
        render_resource_field :title
        render_resource_field :slug
      }

      section("Content") {
        render_resource_field :content
        render_resource_field :excerpt
      }

      section("Publishing") {
        render_resource_field :published_at
        render_resource_field :category
      }

      render_actions
    end

    private

    def section(title, &)
      div(class: "mb-8") {
        h3(class: "text-lg font-semibold mb-4 text-gray-900 dark:text-white") { title }
        fields_wrapper(&)
      }
    end
  end
end

Two-Column Layout

class Form < Form
  def form_template
    div(class: "grid grid-cols-1 lg:grid-cols-3 gap-6") {
      # Main content - 2 columns
      div(class: "lg:col-span-2") {
        fields_wrapper {
          render_resource_field :title
          render_resource_field :content
        }
      }

      # Sidebar - 1 column
      div(class: "space-y-4") {
        Panel {
          h4(class: "font-medium mb-2") { "Settings" }
          render_resource_field :status
          render_resource_field :visibility
        }
      }
    }

    render_actions
  end
end

Field Builder

When using render_resource_field, Plutonium uses the field builder. For custom rendering, use the field method directly.

Basic Field Usage

def form_template
  # Using field builder directly
  render field(:title).wrapped {|f| f.input_tag }
  render field(:content).wrapped {|f| f.easymde_tag }
  render field(:published).wrapped {|f| f.checkbox_tag }

  render_actions
end

Field Builder Methods

The field builder (f) provides these tag methods:

MethodInput Type
f.input_tagText input (auto-detects type)
f.string_tagText input
f.text_tagTextarea
f.number_tagNumber input
f.email_tagEmail input
f.password_tagPassword input
f.url_tagURL input
f.tel_tagTelephone input
f.hidden_tagHidden input
f.checkbox_tagCheckbox
f.select_tagSelect dropdown
f.radio_button_tagRadio buttons

Plutonium-Enhanced Tags

MethodDescription
f.easymde_tag / f.markdown_tagMarkdown editor (EasyMDE)
f.slim_select_tagEnhanced select (Slim Select)
f.flatpickr_tagDate/time picker (Flatpickr)
f.phone_tag / f.int_tel_input_tagInternational phone input
f.uppy_tag / f.file_tagFile upload (Uppy)
f.secure_association_tagAssociation with authorization
f.belongs_to_tagBelongs-to association
f.has_many_tagHas-many association
f.has_one_tagHas-one association
f.key_value_store_tagKey-value pairs

Field with Options

# Select with choices
render field(:status).wrapped { |f|
  f.select_tag(choices: %w[draft published archived])
}

# Date picker with options
render field(:published_at).wrapped { |f|
  f.flatpickr_tag(min_date: Date.today, enable_time: true)
}

# File upload with restrictions
render field(:avatar).wrapped { |f|
  f.uppy_tag(
    allowed_file_types: %w[.jpg .png .gif],
    max_file_size: 5.megabytes
  )
}

Wrapped vs Unwrapped

# Wrapped - includes label, hint, errors
render field(:title).wrapped { |f| f.input_tag }

# Unwrapped - just the input element
render field(:title).input_tag

# Custom wrapper options
render field(:title).wrapped(class: "col-span-full") { |f|
  f.input_tag
}

Input Configuration in Definitions

Define inputs in the definition, render them in the form:

class PostDefinition < ResourceDefinition
  # Configure inputs
  input :title, hint: "Be descriptive", placeholder: "Enter title"
  input :content, as: :markdown
  input :status, as: :select, choices: %w[draft published]
  input :published_at, as: :flatpickr

  # Custom input with block
  input :category do |f|
    choices = Category.active.pluck(:name, :id)
    f.select_tag(choices: choices)
  end

  class Form < Form
    def form_template
      # render_resource_field uses the input configuration
      render_resource_field :title
      render_resource_field :content
      render_resource_field :status
      render_resource_field :published_at
      render_resource_field :category

      render_actions
    end
  end
end

Dynamic Forms (pre_submit)

Fields with pre_submit: true trigger form re-rendering on change:

class PostDefinition < ResourceDefinition
  input :post_type, as: :select,
    choices: %w[article video podcast],
    pre_submit: true

  input :video_url,
    condition: -> { object.post_type == "video" }

  input :podcast_url,
    condition: -> { object.post_type == "podcast" }
end

When post_type changes, the form re-renders via Turbo and shows/hides conditional fields.

Nested Forms

For has_many / has_one associations with accepts_nested_attributes_for:

Model Setup

class Post < ResourceRecord
  has_many :comments
  accepts_nested_attributes_for :comments, allow_destroy: true
end

Definition Setup

class PostDefinition < ResourceDefinition
  nested_input :comments do |n|
    n.input :author_name
    n.input :body, as: :text
  end

  # Or reference another definition
  nested_input :comments, using: CommentDefinition, fields: %i[author_name body]
end

Custom Nested Rendering

class Form < Form
  def form_template
    render_resource_field :title
    render_resource_field :content

    # Nested fields are automatically handled
    render_resource_field :comments

    render_actions
  end
end

Interaction Forms

Forms for interactive actions use Plutonium::UI::Form::Interaction:

class PublishPostInteraction < ResourceInteraction
  attribute :publish_date, :date
  attribute :notify_subscribers, :boolean, default: true

  # Custom form
  class Form < Form
    def form_template
      div(class: "space-y-4") {
        render_resource_field :publish_date
        render_resource_field :notify_subscribers
      }
      render_actions
    end
  end
end

Form Actions

Default Actions

def render_actions
  actions_wrapper {
    # "Create and add another" / "Update and continue editing" button
    # Primary submit button
    render submit_button
  }
end

Custom Actions

def render_actions
  actions_wrapper {
    # Cancel link
    a(href: resource_url_for(resource_class), class: "btn btn-secondary") {
      "Cancel"
    }

    # Save as draft
    button(type: :submit, name: "draft", value: "1", class: "btn") {
      "Save Draft"
    }

    # Primary submit
    render submit_button
  }
end

Form Context

Inside form templates, you have access to:

class Form < Form
  def form_template
    # Form object
    object              # The record
    record              # Alias for object
    object.new_record?  # Check if creating

    # Request context
    current_user
    current_parent
    current_scoped_entity
    request
    params

    # Definition
    resource_definition
    resource_fields     # Permitted fields

    # URL helpers
    resource_url_for(object)
    resource_url_for(Post, action: :new)

    # Rails helpers
    helpers.link_to(...)
  end
end

Theming

Forms use a theme system for consistent styling:

class PostDefinition < ResourceDefinition
  class Form < Form
    class Theme < Plutonium::UI::Form::Theme
      def self.theme
        super.merge({
          fields_wrapper: "grid grid-cols-2 gap-6",
          actions_wrapper: "flex justify-between mt-8",
          input: "w-full p-3 border-2 rounded-lg",
          label: "block mb-1 font-bold text-gray-700"
        })
      end
    end
  end
end

Theme Keys

KeyDescription
baseForm container
fields_wrapperGrid wrapper for fields
actions_wrapperWrapper for buttons
wrapperIndividual field wrapper
labelLabel styling
inputInput styling
hintHint text styling
errorError message styling
buttonSubmit button styling
checkboxCheckbox styling
selectSelect styling
  • plutonium-definition-fields - Input configuration (as:, hint:, condition:)
  • plutonium-views - Custom page classes
  • plutonium-assets - TailwindCSS and component theming
  • plutonium-interaction - Interactive action forms
  • plutonium-nested-resources - Parent/child forms

Score

Total Score

75/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

+10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon