“Building production ready prototypes”
For several years, the author experimented with UI implementations that would create stronger connections between backend processes and frontend development. By designing and developing both ends of applications, relating backend to frontend almost one-to-one became crucial.
This approach yielded an unexpected benefit: the ability to create reusable prototypes that could serve as foundations for production applications, while enabling businesses to conduct A/B testing, toggle features, and implement ad-hoc changes.
Problem
Most UI implementations from 2–4 years prior showed limited progress in addressing core architectural issues. While tools became more sophisticated, offering greater automation and compatibility coverage, they failed to provide genuine simplification or establish foundational building blocks for frontend features.
The typical pattern involved separation by technology rather than separation by concern:
- A folder containing all CSS/SCSS/LESS
- A folder for all templates
- A folder for all JavaScript
These would eventually merge and get minified together. Though JavaScript frameworks like ReactJS promised real module separation, significant conceptual gaps remained, particularly regarding HTML coupling and global CSS scope—until recent tooling improvements.
“We mostly had separation of technologies in our UIs, not separation of concerns.”
The Journey
Two to three years prior, the author began constructing a new UI stack around React and Webpack, designed to align with modern development practices and computer science standards. This stack was engineered as the UI component of a NodeJS-driven system built for Vast.com, supporting a hive-like platform hosting multiple applications with shared purpose but different visual and functional features.
The need arose for:
- Isolated, easily replaceable, replicable, and shareable blocks
- High reusability factors across components
- Alignment with microservices architecture and CQRS + event sourcing principles
Wishlist for the Solution
The author sought to achieve:
- Self-sufficient UI modules containing look, feel, and logic, insulated from external interference
- Strong separation of styles, DOM, and interactions within each module
- Global data handling aligned with CQRS principles
- Modern JavaScript and CSS syntax support
- Highly customizable builds with NodeJS API capabilities for dynamic compilation and server-side rendering
- Live reload and hot module replacement (HMR) functionality
- Portable code suitable for React Native implementation
The Approach: UI Modules
In engineering, understanding components and their assembly relationships remains critical. The author developed a polished UI modules concept built on the recognition that React comprises two fundamental parts: JavaScript logic and a sophisticated data-driven HTML engine (virtual DOM).
This creates a natural triangle completed by CSS:
React = JS + Virtual DOM (HTML) + CSS
Module Structure
The solution organizes each component with three integrated files:
index.js serves as the convergence point, importing a render component and exporting a React class:
import React from 'react';
import DOM from './footer.jsx';
export default class Footer extends React.Component {
constructor(props) {
super(props);
this.view = DOM;
}
render() {
return this.view();
}
}
footer.jsx contains the DOM structure:
import React from 'react';
import styles from './footer.css';
import Logo from '../logo';
export default function() {
return (
<footer className={styles.container}>
<Logo />
<h1 className={styles.title} >This is footer!</h1>
<div className={styles.allRights}>
© 2017 Tessier-Ashpool All rights reserved
</div>
</footer>);
}
footer.css handles styling:
.container {
margin: 30px auto;
width: 97%;
lost-utility: clearfix;
}
.title {
margin: 20px auto;
}
.allRights {
clear: both;
display: block;
padding-top: 65px;
font-size: 16px;
padding-left: 10px;
}
CSS Modules Advantage
CSS Modules parse each related CSS file so that class names become automatically scoped combinations of filename, class name, and hash values—for example: footer__container___1gSYe. This approach provides unlimited specificity while maintaining logical verbosity during development.
<div class="footer__container___1gSYe">
Every module can safely use generic class names like .container and .title without conflicts. Global styles remain possible through :global{} wrapping.
Webpack Implementation
Webpack functions not merely as a build system but as an enabler of core architectural concepts with built-in bonuses.
Handling Imports
Webpack’s loader concept allows custom handlers for different file types, enabling manipulation of content—including image inlining under specified sizes.
CSS Parsing Configuration
The loader setup for CSS Modules:
{
test: /\.css$/,
use:[{
loader: 'style-loader'
},
{
loader: 'css-loader',
options: {
modules:true,
importLoaders: 1,
localIdentName: '[name]__[local]___[hash:base64:5]'
}
},
{
loader: 'postcss-loader'
}
]
}
This configuration transforms import styles from 'some.css' into a registry: {container: 'footer__container__Y56cF'}.
This enables multi-style and multi-DOM UI modules with dynamic variation swapping—useful for A/B testing and feature variations.
Hot Module Replacement
HMR swaps changed code into memory without full page reloads, making development iterations nearly instantaneous and significantly improving developer experience.
NodeJS API Bonus
Webpack provides a NodeJS API enabling dynamic on-the-fly rebuilds. The author utilized this to compile code variations and store them in memory-fs, serving them through API endpoints based on criteria like localization. This proved particularly valuable for complex search widgets delivered to partner sites and for implementing sophisticated debug features.
Data Management
“Everything should be decoupled…everything should receive data through subscription”
Applications depend on smart, flexible data providers. When infrastructure components are properly decoupled through event-driven workflows and subscription-based data delivery, removing or relocating modules causes no breaks or necessary changes. Data listeners simply stop receiving events when components are removed.
Redux as Data Container
Among options like MobX, Relay, GraphQL, Flux, and RxJS, Redux emerged as the author’s choice for its documentation quality, community support, and feature breadth.
Redux functions as a “predictable state container” where events transmit and influence centralized state data through pure functions called reducers. The entire application state resides in one location, with segments connected to specific app parts and modified only through action-triggered reducers.
Store creation combines reducers and middleware:
let store = createStore(
appDataReducers,
applyMiddleware(sagaMiddleware)
);
Redux Provider wraps the application:
const App = ({ store }) => (
<Provider store={store}>
<Router history={ hashHistory }>
<Route path="/" component={ HomePage }></Route>
<Route path="/docs" component={ DocPage }></Route>
<Route path="/examples" component={ ExmplPage }></Route>
</Router>
</Provider>
);
Data Usage Pattern
Each module maintains its own reducer. Modules requiring asynchronous operations have dedicated, separated sagas triggered by related actions. Modules subscribe to their specific data endpoints in the store, completing the data flow.
Complete Technology Stack
View and Glue: React
ReactJS builds views and contains modules. Its virtual DOM combined with reactive state offers unmatched capabilities, proven battle-tested reliability, community support, and comprehensive documentation.
Styling: PostCSS + CSS Modules
PostCSS serves as a CSS manipulation toolkit and preprocessor collection built from modular JavaScript libraries. It handles browser compatibility, prefixing via tools like Autoprefixer, grid systems such as LostGrid, and future CSS specification syntax. Developers can assemble custom CSS toolchains from granular features.
CSS Modules provide a foundational primer for styling, elevating BEM methodology to unlimited specificity heights while dramatically improving developer experience.
Data Management: Redux + Redux-Saga
Redux manages all application data through event-driven state containers. Redux-Saga complements this with handling for extended operations (sagas)—primarily AJAX calls—enabling full reactive, event-sourcing-aligned, CQRS-inspired architecture.
Bundling: Webpack
Webpack handles packaging and transpilation with seamless implementation of complex asset loading and parsing strategies.
Additional Libraries
- React Router: Client-side routing with strong current support
- Axios: AJAX library (author plans future migration to Fetch API)
- Jest: Testing framework with speed, power, integrated coverage reporting, and stack alignment
Recent Applications
This stack has undergone extensive real-world testing across numerous applications and websites in recent years, proving efficient and highly reusable. It has demonstrated particular strength when adapted for React Native development.
The original wishlist was fully satisfied.
Code Repository
A boilerplate repository containing this approach:
The repository includes solutions for:
- Production and development builds
- Testing
- Coverage reports
- Build analytics
- Simple web server deployment
- Update checking