- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
Making Your Web App Work Well on Slow Mobile Networks
About Post
Your app is fast. You know it's fast, because you use it every day: on office Wi-Fi, on a recent laptop, with the server a few milliseconds away.
Your users are in a lift. Or a basement car park. Or on a train between two towers, with one bar of signal and a phone that's three years old. For them, the same app is a white screen, a spinner, and a button that may or may not have done something.
Building for slow networks isn't a niche concern. It's the normal condition of mobile use, at least some of the time, for nearly everyone. Here's what makes the biggest difference, roughly in the order I'd tackle it.
First, feel the pain yourself
You won't fix what you never experience. Before changing any code, use your app on a bad connection:
- In Chrome or Edge DevTools, the Network tab has throttling presets for slow mobile connections. Turn one on and reload your main screens.
- Android emulators let you set network speed and latency; on iOS, Apple's Network Link Conditioner does the same on a device.
- Even better, walk somewhere with poor signal and try the critical flows on a real phone.
It's humbling. Things you never noticed, like five requests firing one after another before a screen appears, suddenly take seconds each.
Latency hurts more than bandwidth
Here's the insight that changes how you design: on mobile, the killer is often not how many bytes you send, but how many round trips you make. Every request has to travel to the server and back, and on a weak mobile connection each trip can take a long, unpredictable time.
So a screen that needs one 40 KB response usually beats a screen that needs six 5 KB responses made one after another. Watch out for waterfalls: fetch the user, then use their ID to fetch their account, then use that to fetch the list. Each step waits for the one before it.
- Design API endpoints around screens, so a screen can load with one or two requests.
- Fire independent requests in parallel instead of in sequence.
- Avoid redirects on the critical path; each one is another round trip.
Send less
Bytes still matter, especially on metered data. The usual suspects:
- Bloated API responses. Returning every column of every related model "just in case". Use API Resources (or whatever your framework offers) to return only what the screen needs, and paginate lists.
- Images. Usually the heaviest thing on the page. Resize to the size you display, use WebP or AVIF, and lazy-load what's below the fold.
- JavaScript bundles. Split by route so the first screen doesn't download the code for every other screen.
- Uncompressed text. Make sure the server compresses HTML, CSS, JS and JSON with gzip or Brotli. It's often a one-line server setting with a large effect.
Don't download the same thing twice
The fastest request is the one you never make. Static assets with hashed file names (which Vite produces by default) can be cached by the browser for a very long time, because a new build gets a new file name.
For API data that doesn't change often, conditional requests help: the server sends an ETag, the client sends it back next time, and if nothing changed, the server replies 304 Not Modified with no body. Laravel has middleware for cache headers:
Route::middleware('cache.headers:private;max_age=300;etag')
->get('/api/settings', SettingsController::class);
Use private for anything user-specific so shared caches don't store it, and keep max_age short for data that changes.
Make waiting feel shorter
You can't make every response instant, but you can make the app feel responsive:
- Skeleton screens show the shape of the content while it loads. They feel faster than a spinner because the user can see what's coming.
- Show cached data first, refresh in the background. Yesterday's list right now, updated a second later, beats an empty screen.
- Optimistic updates for low-risk actions: mark the message as read or the item as liked immediately, and quietly roll back if the request fails. Don't do this for payments or anything the user must be sure of.
- Immediate feedback on every tap. A button that doesn't react for two seconds gets tapped again.
The principle: the user should never wonder whether the app heard them. Acknowledge every action instantly, even if the real work takes a while.
Expect requests to fail
On a weak connection, requests time out, fail halfway, or succeed without the response ever arriving. Plan for all three:
- Set timeouts. Without one, a request can hang far longer than any user will wait. On the web,
fetch(url, { signal: AbortSignal.timeout(15000) })gives up after 15 seconds. - Retry safely. Retry reads automatically with backoff. For actions that create things (a payment, a maintenance request), send an idempotency key so a retry can't create a duplicate.
- Keep the user's input. If a form submission fails, never clear the form. Losing a long message to a dropped connection is how apps get one-star reviews.
- Write honest error messages. "No connection. Your request is saved and will be sent when you're back online" is far better than "Something went wrong".
Offline hints: tell people what's happening
When the connection drops completely, say so with a small, calm banner, rather than letting every action fail mysteriously.
In the browser, the online and offline events and navigator.onLine help, with one catch: false reliably means offline, but true only means the device has a network, not that the internet actually works. Treat failed requests as the real signal.
In React Native, which we use for the Kate PMS tenant app, the community NetInfo library gives you a richer picture:
import NetInfo from '@react-native-community/netinfo';
const unsubscribe = NetInfo.addEventListener((state) => {
// isInternetReachable can be null while it's still checking
setOffline(state.isConnected === false || state.isInternetReachable === false);
});
For apps that must work fully offline, there's a further step: queuing actions locally and syncing later (on the web, with a service worker). It's powerful but adds real complexity around conflicts, so do it for the flows that genuinely need it, not by default.
The checklist
- Test on a throttled connection and a real phone with poor signal.
- Cut round trips: screen-shaped endpoints, parallel requests, no waterfalls.
- Send less: lean JSON, compressed text, resized modern images, split bundles.
- Cache aggressively and use ETags for data that rarely changes.
- Skeletons, cached-first rendering and instant feedback on every tap.
- Timeouts everywhere, safe retries with idempotency keys, never lose user input.
- A calm offline banner, and real offline support only where it's needed.
Where do your users struggle with connection most: lifts, basements, trains, or somewhere more surprising? Knowing that changes which of these you fix first.

Be first to comment it...