!NOTE: this guide currently only covers the first phase. The Vite setup and migration are not detailed here.
This guide documents our approach to migrating from Webpacker to a modern frontend asset pipeline. We implemented a phased strategy:
- Phase 1 (Current): Replace Webpacker with CDN-based resources
- Phase 2 (Future): Migrate to Vite for a modern build system
Webpacker presented several challenges:
- No longer actively maintained
- Slow build process
- Added complexity to the application
- Difficult to upgrade
Our CDN implementation uses a parallel layout approach that allows for:
- Side-by-side testing of Webpacker and CDN implementations
- Gradual migration of controllers to the CDN layout
- Easy rollback if issues are encountered
# app/controllers/concerns/cdn_layout.rb
module CdnLayout
extend ActiveSupport::Concern
included do
layout 'application_cdn'
end
end
This concern can be included in any controller to switch it to the CDN layout:
class ApplicationController < ActionController::Base
# Uncomment to use the CDN-based layout
# include CdnLayout
# ...
end
<!-- app/views/layouts/application_cdn.html.erb (simplified) -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta tags and Rails-specific tags -->
<!-- Fomantic UI CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.css">
<!-- DataTables with Fomantic UI styling -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.semanticui.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.5.0/css/responsive.semanticui.min.css">
<!-- Application CSS -->
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<!-- Fomantic UI JS -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/semantic.min.js"></script>
<!-- DataTables JS -->
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.semanticui.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.5.0/js/dataTables.responsive.min.js"></script>
<!-- Turbolinks -->
<%= javascript_include_tag 'turbolinks', 'data-turbolinks-track': 'reload' %>
<!-- Application JS -->
<%= javascript_include_tag 'application_cdn', 'data-turbolinks-track': 'reload' %>
</head>
<body>
<!-- Layout content -->
</body>
</html>
// app/assets/javascripts/application_cdn.js
document.addEventListener("turbolinks:load", function() {
// Initialize Fomantic UI components
$('.ui.dropdown').dropdown();
$('.ui.accordion').accordion();
$('.ui.checkbox').checkbox();
$('.ui.modal').modal();
$('.ui.popup').popup();
$('.ui.rating').rating();
$('.ui.sidebar').sidebar();
$('.ui.sticky').sticky();
$('.ui.tab').tab();
$('.ui.toast').toast();
// Initialize DataTables if present
if ($.fn.DataTable && $('#data_table').length) {
$('#data_table').DataTable({
responsive: true,
dom: 'lfrtip',
lengthMenu: [[10, 25, 50, 100], [10, 25, 50, 100]],
pageLength: 10,
order: [[0, 'desc']],
language: {
search: "_INPUT_",
searchPlaceholder: "Search records...",
lengthMenu: "Show _MENU_ entries",
info: "Showing _START_ to _END_ of _TOTAL_ entries",
infoEmpty: "Showing 0 to 0 of 0 entries",
paginate: {
first: '<i class="angle double left icon"></i>',
previous: '<i class="angle left icon"></i>',
next: '<i class="angle right icon"></i>',
last: '<i class="angle double right icon"></i>'
}
},
columnDefs: [
{ className: "center aligned", targets: [0, 3] }
],
drawCallback: function() {
$('.paginate_button').addClass('ui button');
$('.paginate_button.current').addClass('primary');
$('.dataTables_length select').addClass('ui dropdown');
$('.dataTables_filter input').addClass('ui input');
}
});
}
});
// Turbolinks integration - prevent memory leaks
document.addEventListener('turbolinks:before-cache', function() {
if ($.fn.DataTable && $.fn.DataTable.isDataTable('#data_table')) {
$('#data_table').DataTable().destroy();
}
});
# app/controllers/cdn_test_controller.rb
class CdnTestController < ApplicationController
include CdnLayout
def index
# This controller uses the CDN layout
end
end
# config/routes.rb
Rails.application.routes.draw do
# CDN Test route
get 'cdn_test', to: 'cdn_test#index'
# Other routes...
end
/* DataTables Styling */
.dataTables_wrapper {
padding: 0.5em 0;
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
padding: 0.5em 1em;
margin-left: 2px;
border: 1px solid transparent;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current {
background: #2185d0;
color: white !important;
}
/* Status Indicators */
.center.aligned i.icon.green,
.center.aligned i.icon.red {
font-size: 1.2em;
}
/* Responsive Adjustments */
@media only screen and (max-width: 767px) {
.ui.container {
width: auto !important;
margin-left: 1em !important;
margin-right: 1em !important;
}
}
- Create Controller Concern: Implemented a reusable controller concern
- Create CDN Layout: Developed a new layout file with CDN resources
- Add JavaScript Initialization: Created dedicated JS for component initialization
- Create Test Controller: Added a controller and view for testing
- Enhance CSS: Added CSS improvements for better styling
- Test Implementation: Verified all components work correctly
- Visit
/cdn_test
to see a page using the CDN-based layout - Compare with regular pages to ensure everything looks correct
To switch the entire application to use the CDN-based layout:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Uncomment to use the CDN-based layout
include CdnLayout
# ...
end
If you encounter issues with the CDN approach:
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# Comment out to use the default Webpacker layout
# include CdnLayout
# ...
end
We implemented a comprehensive testing approach:
- Component Testing: Verify all Fomantic UI components render correctly
- DataTables Testing: Ensure tables initialize properly with Fomantic UI styling
- Event Handling: Test JavaScript event handlers work as expected
- Responsive Design: Verify responsive behavior across different device sizes
- Layout Switching: Test switching between standard and CDN layouts
-
Performance
- Faster page loads (CDN assets are often cached by browsers)
- Reduced server load for asset compilation
-
Development
- Simplified development environment
- No need to run Webpacker in development
- Faster feedback cycle
-
Maintenance
- Reduced dependency complexity
- Access to the latest Fomantic UI features and fixes
- Easier upgrades
After successfully implementing the CDN approach:
- Remove Webpacker dependencies from the application
- Update the package.json file to remove unused packages
- Implement Vite for local asset management
- Gradually migrate CDN resources to locally managed assets