Veeqo has Ruby on Rails in its DNA. Rails is the technology that enabled its growth from the early days of the platform and keeps on supporting new features and quick iterations as the service scales.
Rails, as a battle-tested framework, provides solid tools to develop apps throughout the stack. But for a long time, the evolution of its front-end features didn't move as fast as the rest of the programming ecosystem. When the JavaScript world discovered the pleasures of modern paradigms such as React and Vue, Rails developers still had to rely on an aging asset pipeline and Ajax calls to bring reactivity to their applications. The adoption of Webpacker made a big difference and enabled a much needed upgrade in Rails front-end capabilities, via the use of modern JavaScript tools. The recent release of Stimulus pushes the evolution even further, but it is now easier than ever to add a JavaScript framework to your Rails application. At Veeqo, we settled on React.
To be more specific, Veeqo's front-end is currently experiencing an extraordinary revolution.
With the growth of our development team and the launch of new features, Veeqo understood the importance of ensuring a consistent visual language and started developing its own design-system, rationalizing the use of colors, typography and reusable components. The creation of our design system will most likely be the topic of an upcoming article. But this componentization is also what led to the introduction of React within our development process.
Veeqo's front-end landscape
Veeqo currently uses three major tools to manage its front-end:
Rails views
These are the canonical ways of displaying data in a Rails application. Based on a templating language called ERB (other templates can be used), they allow to structure views using HTML, while injecting Ruby code into the markup.
Backbone Marionette
Marionette is what we now refer to as our legacy system. A big part of Veeqo's core logic is still based on this framework, coming from the Backbone ecosystem. We use it as an interface to handle any complex requests or events happening client side. In particular, Veeqo used Marionette to build a powerful API attached to the client's window object, providing data on products, orders and accounts information across the application. However Marionette has one drawback: its age. It appeared nearly 10 years ago (pre-React) and uses a complex MVC architecture as well as older templates like CoffeeScript. While still being powerful and capable, these aspects make Marionette difficult to pick up for new Veeqo developers, which also motivated our transition towards React.
React
ReactJS no longer needs introduction in 2021. Originating from Facebook, React quickly became the leading front-end framework we know for a number of reasons:
- It is purely based on JavaScript, making it easy to pick up for a majority of developers
- It has componentazition in its core, making it a strong ally to any design system
- It offers great developer tools and benefits from a rich ecosystem, offering seamless integration with thousands of packages and actively developed projects
This also means it is much faster for new Veeqo talents to grasp the different aspects of the codebase and quickly contribute to it.
How does Veeqo integrate with React?
Numerous ways exist to add React to a Rails application. Older projects used to rely on dedicated gems such as react-rails or react-on-rails. Since 2017 and the introduction of the Webpacker gem, Rails now benefits from a seamless integration with webpack, bundling JavaScript files and feeding them to the Rails asset pipeline.
Veeqo developers currently use two different techniques to build their React views.
1) The hybrid way
Veeqo is a product-focused company. This means that our development efforts follow the requirements dictated by our product and design teams. Keeping things agile, the Veeqo platform is constantly changing, testing new features and iterating on ideas. This implies changing only portions of our views, like on our new homepage.
Technically speaking, this requires the new React features to live alongside the legacy Rails views, without breaking the existing functionalities. For this use case, the ERB template offers a great solution: the possibility to inject some JavaScript into our markup via a <script>
tag.
Let's take the example of a sidebar, as recently introduced with the big redesign of the Veeqo application.
In a pure Rails app, the view (extremely simplified) would be as follows:
<body>
<%= render partial: '/layout/sidebar', , locals: { user: @user } %>
<div>
<p>Content</p>
</div>
</body>
The sidebar is being injected via a partial (similar to an ERB component), which receives user data from the instance variable @user
.
In a similar fashion, as our brand new Sidebar is now a React component imported from our design system, we can simply inject it using a <script>
tag:
<body>
<div id="sidebar-container"></div>
<div>
<p>Content</p>
</div>
<script>
ReactDOM.render(
React.createElement(window.SidebarComponent, {
user: <%= raw @user.to_json %>
}), document.querySelector('#sidebar-container')
);
</script>
</body>
This uses the traditional way of injecting React into a web page. Despite being a powerful framework, React really simply is a way to organize JavaScript files. It needs to identify a target (#sidebar-container
) in which to inject the newly created component (window.SidebarComponent
).
The beauty lies in the combination of JavaScript and ERB. The <%= %>
tag gives us the ability to pass data from our Rails controller to the React component as props, at mount time. The data can then be used by the JS component. This supposes the creation of the said component beforehand, using packs
as specified in the webpacker documentation.
const Sidebar = ({ user }) => {
return (
<div>
<h1>Hello {user.name}!</h1>
</div>
);
};
window.SidebarComponent = SettingsPage
export default Sidebar;
As described, this way of injecting React works fine in a hybrid setup, when small react components need to live alongside legacy components. Data flows from the Rails backend to the ERB view, passing info to the React component via props on mount time.
But what if we need to pass data back to the server? What if our component needs to update following an Ajax event?
2) The API way
The API option is the architecture you will find in most modern Rails/React applications. It is very close to what became a standard in modern web development: Rails drops its views and solely acts as a back-end server with an API interface. The front-end is therefore dedicated to React, which gets and sends data to the Rails server via API requests.
This brings many benefits: back-end and front-end can be developed in isolation and quickly update their architecture to new languages and features. However this design also implies more monitoring and potentially higher costs (multiplication of hosts, deployment processes, points of failure etc).
At Veeqo, we opted for a middle ground. We took complex parts of the application, such as the Orders and Inventory pages, and turned them into independent React applications. They still live within the Rails monolith, but do not communicate via ERB anymore. They use API endpoints.
In a similar manner, an anchor is created in a Rails view to inject the React app into it, acting as an empty shell. Once mounted, the app behaves like a single page application and uses a range of tools to operate:
- MobX for advanced state management
- API calls to get and send data to the Rails API
- The existing Marionette API providing access to additional data via the window object
Once again, all of this magic is made available by Webpacker, bundling our complex React files and relationships into JavaScript instructions readable by the Rails asset pipeline.
This architecture helped our front-end teams to move faster and more independently, focusing on bringing major updates to the platform and laying ground to future enhancements. Like every technical decision, it also brought new challenges, through the adoption of new testing processes (Jest vs Rspec) and the integration of new development tools (Lerna, Rush...).
Like the needs of our customers, Veeqo evolves with time, constantly picking up new tools able to bring value to our platform. The front-end revolution is on the march at Veeqo, and we are beyond excited for the great updates coming for our users!