The Problem
A logistics company needed a complete courier management platform to handle shipment tracking, dispatch workflows, route optimization, and reporting — all while supporting 5+ languages (Urdu, Arabic, Turkish, Farsi, Persian) with right-to-left (RTL) and left-to-right (LTR) text rendering. The platform had to scale to hundreds of daily shipments with real-time tracking and role-based dashboards for admins and supervisors.
What Made It Hard
Internationalization (i18n) with RTL/LTR
Supporting multiple languages isn't just translation:
- RTL languages (Arabic, Urdu, Farsi) flip entire layout
- Components must mirror (sidebar moves from left to right)
- Icons and arrows need direction-aware rendering
- Forms, tables, and date pickers must adapt
- Mixed content: English tracking ID in Arabic UI
Challenge: Building a component library that adapts to text direction without duplicating code.
Performance with Large Datasets
Admin dashboards displayed:
- 500+ shipments per page
- Real-time status updates every 30 seconds
- Excel exports with 10,000+ rows
- Complex filtering (date range, status, courier, route)
- Map view with 200+ simultaneous pins
Problem: React re-renders entire table on every update → UI freezes.
Role-Based Access Control (RBAC)
Two user types with different permissions:
- Admin: Full access (create, edit, delete shipments, manage users)
- Supervisor: Read-only dashboards, can update status, no deletion
Complexity:
- Permissions checked on every API call (backend) and UI element (frontend)
- Some features partially restricted (supervisor can update status but not edit customer details)
- Audit trail required for compliance
My Solution
Comprehensive Internationalization Architecture
Built direction-aware component system using i18next and CSS logical properties:
// Language configuration
const languages = {
en: { dir: 'ltr', label: 'English' },
ar: { dir: 'rtl', label: 'العربية' },
ur: { dir: 'rtl', label: 'اردو' },
tr: { dir: 'ltr', label: 'Türkçe' },
fa: { dir: 'rtl', label: 'فارسی' },
};
// Component adapts to direction
function ShipmentCard({ shipment }: Props) {
const { t, i18n } = useTranslation();
const isRTL = i18n.dir() === 'rtl';
return (
<div className={`card ${isRTL ? 'rtl' : 'ltr'}`}>
<div className="card-icon" style={{
[isRTL ? 'marginLeft' : 'marginRight']: '12px'
}}>
<ChevronIcon direction={isRTL ? 'left' : 'right'} />
</div>
<h3>{t('shipment.title')}</h3>
</div>
);
}
CSS Approach:
- Used CSS logical properties (
margin-inline-startinstead ofmargin-left) - Direction-agnostic spacing (
padding-inline,border-inline) - Global
[dir="rtl"]selector for layout flips - Bootstrap RTL stylesheet loaded conditionally
Translation Management:
- 80+ API endpoints translated (success/error messages)
- Centralized translation files per language (
en.json,ar.json) - Context-aware translations (e.g., "Delivered" = masculine/feminine in Arabic)
Performance Optimization Strategy
Implemented virtualization + memoization + lazy loading:
1. Virtual Scrolling for Tables
- Used
react-windowto render only visible rows - 500 shipments → only 20 DOM nodes (massive performance gain)
- Smooth scrolling even with 10,000 rows
2. Memoization with React.memo
const ShipmentRow = React.memo(({ shipment }: Props) => {
// Only re-renders if shipment data changes
return <tr>...</tr>;
}, (prev, next) => prev.shipment.id === next.shipment.id);
3. Lazy Loading for Heavy Components
- Map view loaded on-demand (not part of initial bundle)
- Excel export runs in Web Worker (non-blocking)
- Chart libraries code-split
4. Optimistic Updates for Status Changes
- Update UI immediately (instant feedback)
- Sync with backend in background
- Rollback if API fails
Results:
- 60% reduction in initial load time
- Table interactions remain smooth at 500+ rows
- Excel export doesn't freeze UI (Web Worker)
Role-Based Access Control System
Built declarative permission system:
// Backend middleware
function requirePermission(permission: Permission) {
return (req, res, next) => {
const user = req.user;
if (!user.hasPermission(permission)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
}
// Route example
router.delete('/shipments/:id',
requirePermission('delete_shipment'),
deleteShipment
);
// Frontend HOC
function withPermission(Component, permission) {
return (props) => {
const user = useAuth();
if (!user.hasPermission(permission)) {
return <AccessDenied />;
}
return <Component {...props} />;
};
}
Permission Granularity:
- Admins:
create_shipment,edit_shipment,delete_shipment,manage_users - Supervisors:
view_shipments,update_status
UI Adaptation:
- Delete button hidden for supervisors
- Edit forms show as read-only
- Bulk actions disabled for non-admins
Real-Time Tracking with QR Codes
Implemented QR-based shipment tracking:
- Each shipment gets unique QR code (generated at dispatch)
- Customers scan QR to see live status
- Status updates push to tracking page via polling
- SMS notifications with tracking link
Tech Stack:
- QR generation:
qrcode.react - Tracking page: Public route (no auth required)
- Status updates: Long-polling (30s interval)
Advanced Reporting & Export
Built flexible reporting system:
- Date range filters (last 7 days, last month, custom)
- Multi-select filters (status, courier, route)
- Excel export with formatting (headers, colors, totals)
- PDF reports for invoicing
Excel Export Challenge:
- 10,000+ rows cause memory issues
- Solution: Streaming export (chunks of 500 rows)
- Web Worker to avoid blocking UI
- Download link generated when ready
Technical Decisions
Why i18next over react-intl?
- Better interpolation for complex plurals (Arabic has 6 plural forms)
- Namespacing for large translation files
- Lazy loading of translations per route
- Context-aware translations (gender, case)
Why React-Window over React-Virtualized?
- Smaller bundle size (2KB vs 26KB)
- Simpler API
- Better performance for our use case
- Active maintenance
Why Bootstrap over Material-UI?
- Client requirement (existing design system)
- RTL support built-in with
bootstrap-rtl - Familiarity for team (faster development)
- Customizable via SASS variables
Why PostgreSQL over MongoDB?
- Relational data (shipments → routes → couriers)
- ACID compliance for financial records
- Strong TypeScript integration with Sequelize
- Full-text search for shipment tracking
Impact
Business Metrics
- 500+ contributions over 2+ years
- 80+ API integrations across admin and tracking portals
- 5+ languages with full RTL/LTR support
- 100-500 users daily (admins, supervisors, customers)
Technical Achievements
- 60% faster load times through lazy loading and code splitting
- Zero layout bugs in RTL languages (comprehensive testing)
- Sub-200ms API response times for real-time tracking
- 10,000+ row Excel exports without UI freeze (Web Workers)
Developer Experience
- Reusable i18n component library
- Permission system prevents unauthorized access
- Comprehensive Storybook for component demos
- Automated E2E tests for critical workflows
What I'd Do Differently
Replace Bootstrap with Tailwind
Would migrate to Tailwind CSS:
- Built-in RTL support with
dir="rtl"variant - No runtime CSS (better performance)
- Smaller bundle (purge unused styles)
- Current Bootstrap approach works but CSS is heavy
GraphQL for Complex Dashboards
Would explore GraphQL instead of REST:
- Dashboard needs 5+ API calls (shipments, routes, couriers, stats, notifications)
- GraphQL reduces over-fetching
- Single endpoint, better caching
- Current REST approach has N+1 query problem
Server-Sent Events for Real-Time Updates
Would replace polling with SSE:
- 30-second polling is crude (wastes bandwidth)
- SSE pushes updates instantly
- Lower latency, better UX
- Fallback to polling for old browsers
TypeScript from Day One
Would enforce strict TypeScript from start:
- Current codebase is mix of JS and TS
- Type safety prevents runtime errors
- Better autocomplete and refactoring
- Migration mid-project was painful
Key Takeaways
- RTL is more than mirroring - requires direction-aware components and logical CSS properties
- Virtualization is essential for large datasets - render only what's visible
- RBAC must be enforced at both API and UI layers - frontend-only checks are security theater
- Web Workers unlock non-blocking exports - heavy processing shouldn't freeze UI
- i18n is an architecture decision - retrofitting internationalization is expensive