Skeleton Loader is a Ruby on Rails gem for creating animated placeholders that enhance loading states. Whether rendered through Rails views or dynamically with JavaScript, these skeletons provide a seamless visual experience while content loads.
âš Note: Skeleton Loader is an experimental gem currently in early development, exploring the possibilities in the magical world of Rails gems. While loaders are ideally handled client-side, this gem aims to make it easy to add placeholders directly within the Rails realm.
- Features
- Installation
- Quick Start
- Usage
- JavaScript Integration
- Configuration
- Code Quality
- Roadmap
- Contributing
- License
- 🚀 Seamless Rails integration: Enhances loading transitions with minimal setup.
- 🔨 Universal skeletons: Creates loading states in Rails views & JavaScript.
- ⚙️ Consistency: Unified templates & options across both environments.
- ⚡ Lightweight & fast: Designed to be fast and easy to implement with minimal dependencies.
To install Skeleton Loader, add the following line to your Gemfile:
gem "skeleton-loader"Then, execute:
bundle install- Ruby 2.5 or higher
- Rails 5.0 or higher
- Asset Pipeline (for CSS handling)
Add predefined templates to your app with:
rails generate skeleton_loader:add_templatesThis command will place templates in app/views/skeleton_loader/, which you can customize as needed.
And in case needed, you can revert to default templates by running:
rails generate skeleton_loader:reset_templatesQuickly integrate Skeleton Loader into your Rails app with these steps:
-
Include Assets
-
Rails 7 (Import Maps):
- In
config/importmap.rb, add:pin "skeleton-loader", to: "skeleton_loader.js"
- In your JavaScript entry file, add:
import SkeletonLoader from "skeleton-loader"
- In
-
In
app/assets/stylesheets/application.css, add:*= require skeleton_loader
-
Rails 6 (Webpack):
- Run:
npm install "@ersync/skeleton-loader" - In your JavaScript entry file, add:
import SkeletonLoader from "skeleton-loader"
- Run:
-
Rails 5 (Asset Pipeline):
- In
app/assets/javascripts/application.js, add://= require skeleton_loader - In
app/assets/stylesheets/application.css, add:*= require skeleton_loader
- In
-
-
Use in Views
Use the
skeleton_loaderhelper next to the content that loads slowly. Setcontent_idto the target element's ID and choose atypefor the skeleton template.Example:
<div id="content-element"> <!-- Content that takes time to load --> </div> <%= skeleton_loader(content_id: 'content-element', type: "card") %>
For more detailed instructions, refer to Usage section.
The gem provides a primary view helper, skeleton_loader, which generates a div containing a skeleton next to the targeted content element. This skeleton will be hidden once the content element fully loads, providing a seamless loading experience.
Specify content_id and, optionally, a template type and customization options.
<%= skeleton_loader(content_id: 'content-element',
type: "card",
count: 5,
scale: 1.2,
animation_type: 'animation-pulse') %>Define your own HTML structure in a block. Note that type and options are not used.
<%= skeleton_loader(content_id: 'content-element') do %>
<div class="custom-skeleton">
<div class="avatar skeleton-circle"></div>
<div class="text-lines"></div>
</div>
<% end %>See Configuration for all available options.
Skeleton Loader also provides a JavaScript API to dynamically create skeletons independently of Rails views.
đź’ˇ You only need this API if you're loading content asynchronously i.e. AJAX calls. For standard Rails views, use the Rails helper method instead.
First, initialize the SkeletonLoader in your JavaScript:
const skeleton = new SkeletonLoader();There are two methods for creating skeletons using JavaScript:
const loader = skeleton.render({
contentId, // Required. ID of the element to replace with skeleton
...options // Optional. See configuration section for available options
});const loader = skeleton.renderCustom({
contentId, // Required. ID of the element to replace with skeleton
markup // Required. Custom HTML string for skeleton content
});Each skeleton instance (both render() and renderCustom()) returns an object with these methods:
isLoading(): Returns the current loading statereveal(): Removes skeleton and displays content
// Check if still loading
if (loader.isLoading()) {
// Do something while content loads
}
// Remove skeleton and show content
loader.reveal();Practical Examples
Here's some example showing how to use the skeleton loader with an API call:
async function loadUserProfile(userId) {
// Create skeleton while loading
const loader = skeleton.render({
contentId: 'content-element',
type: 'profile'
});
try {
// Fetch your data
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
// Update the DOM with your content
document.getElementById('content-element').innerHTML = createUserProfile(userData);
// Remove the skeleton
loader.reveal();
} catch (error) {
console.error('Failed to load user profile:', error);
loader.reveal(); // Always cleanup the skeleton
}
}async function loadDashboard() {
// Create multiple skeletons
const loaders = {
profile: skeleton.render({ contentId: 'profile', type: 'profile', scale:1.2, width:250 }),
stats: skeleton.render({ contentId: 'stats', type: 'card', scale:1.3, animationType:"sl-flow" }),
activities: skeleton.render({ contentId: 'activities', type: 'list' })
};
// Load your data
const [profileData, statsData, activitiesData] = await Promise.all([
fetchProfile(),
fetchStats(),
fetchActivities()
]);
// Update content and reveal each section
updateProfile(profileData);
loaders.profile.reveal();
updateStats(statsData);
loaders.stats.reveal();
updateActivities(activitiesData);
loaders.activities.reveal();
}The following options can be passed to both the Rails view helper and JavaScript API. Note that Rails uses snake_case (e.g., animation_type), while JavaScript uses camelCase (e.g., animationType).
| Option | Type | Description | Default | Example |
|---|---|---|---|---|
content_id |
String | Unique identifier for the skeleton loader container | nil |
"content-element" |
type |
String | Predefined template type (e.g., "product", "profile") | "default" |
"product" |
width |
Integer | Base width of a single skeleton item in pixels | Depends on type |
250 |
count |
Integer | Number of skeleton items to render | Depends on type |
6 |
per_row |
Integer | Number of skeleton items displayed per row | Depends on type |
4 |
scale |
Float | Size multiplier for all skeleton item dimensions | 1.0 |
1.2 |
animation_type |
String | Type of loading animation | "sl-gradient" |
"sl-glow" |
Notes:
typedetermines default layout and stylingscaleaffects width and spacing proportionallyanimation_typesupports different loading effect styles
Skeleton Loader comes with several pre-built templates, each with their default configurations:
| Template Type | Default Width | Count | Items Per Row |
|---|---|---|---|
card |
200px | 3 | 3 |
comment |
900px | 2 | 1 |
default |
900px | 1 | 1 |
gallery |
320px | 3 | 3 |
paragraph |
900px | 1 | 1 |
product |
320px | 3 | 3 |
profile |
320px | 3 | 3 |
For an interactive preview of available templates and animations, visit the Live Demo.
Choose from several animation styles to match your design:
sl-gradient(default): Smooth gradient movementsl-shine: Shimmer effectsl-pulse: Fade in/outsl-flow: Continuous flowsl-neon: Subtle glowsl-breathing: Gentle scaling
To set application-wide defaults, create config/initializers/skeleton_loader.rb:
SkeletonLoader.configure do |config|
# Override default template settings
config.templates[:product] = {
width: 400,
count: 6,
per_row: 3
}
# Global settings
config.scale = 1.0 # Default: 1.0
config.animation_type = "sl-gradient" # Default: "sl-gradient"
endFor applications requiring custom HTML elements or styles:
SkeletonLoader.configure do |config|
config.additional_allowed_tags = [] # Default: []
config.additional_allowed_attributes = {} # Default: {}
config.additional_allowed_css_properties = [] # Default: []
endSkeleton Loader maintains code quality through:
- RSpec: Comprehensive tests covering core functionality.
- CircleCI: Continuous integration ensures all new changes meet quality standards.
- Rubocop: Consistent linting aligns code with Ruby community conventions.
Here are some features I'd like to add when I have time:
- New Templates & Animations: Expanding template and animation options.
- Turbo & Stimulus Support: Enhancing compatibility with Rails Turbo and StimulusJS.
- Builder Helper for Custom Skeletons:: Introducing a helper to easily create custom skeletons (e.g., circles, rectangles, etc.) with customizable options, simplifying the process of designing custom skeletons.
Suggestions and contributions are welcome!
To contribute:
- Fork the repository.
- Create a new feature branch.
- Add tests for new features.
- Commit your changes and submit a pull request.
Please follow the Code of Conduct for all contributions.
Skeleton Loader is licensed under the MIT License. See LICENSE for details.