Funicular Image Uploader
Image upload component for Funicular.
The plugin only owns the browser-side interaction: file selection, object URL preview, optional FormData upload, and display of the current image URL. The Rails app owns persistence. The same component works with a controller that stores bytes in SQLite or one that attaches the upload to Active Storage.
Install
group :funicular do
gem "funicular-image-uploader"
end
Render plugin assets from the Rails layout:
<%= funicular_plugin_include_tags %>
Deferred upload with a profile form
Use on_select when an image should be submitted together with other form
fields:
component(
Funicular::Plugins::ImageUploader::Component,
src: current_user.has_avatar ? "/users/#{current_user.id}/avatar" : nil,
input_id: "avatar-input",
file_field: "avatar",
image_class: "profile-avatar-image",
input_class: "profile-avatar-input",
on_select: ->(file, preview_url) { @selected_avatar_file = file }
)
If no classes are supplied, the plugin's default CSS styles the file selector, clear button, and upload button. Applications can pass class props when they need to align the component with their own design system.
The parent component can then send its form data with
Funicular::FileUpload.upload_with_formdata.
Standalone upload
Use auto_upload: true when the image has its own endpoint:
component(
Funicular::Plugins::ImageUploader::Component,
src: "/users/#{current_user.id}/avatar",
upload_url: "/users/#{current_user.id}/avatar",
file_field: "avatar",
auto_upload: true,
on_upload: ->(result) { puts "uploaded" },
on_error: ->(, result) { puts }
)
The endpoint should return JSON. If it includes image_url, the component uses
that URL after upload. Override the key with response_url_key: "url".
Active Storage endpoint example
class Users::AvatarsController < ApplicationController
before_action :require_login
def update
current_user.avatar.attach(params[:avatar])
render json: {
image_url: rails_blob_path(current_user.avatar, only_path: true)
}
end
end
resource :avatar, only: [:update], controller: "users/avatars"
For direct SQLite storage, read params[:avatar] in the controller and return
the URL that serves the stored bytes.