Mert
My CLI was slow — then I stopped awaiting everything
I build a CLI that scans a directory full of projects and runs npm audit –json on each one to find vulnerabilities. It worked but it felt slow. Every project waited for the previous one to finish before starting.
I was doing something like this (roughly):
for (const dir of dirs) {
await runAudit(dir)
}
The problem is each npm audit call was waiting for the one before it to finish. All of them could run at the same time, but I was forcing them to run one by one.
The fix Promise.all
Instead of awaiting each call inside the loop, I stored all the promises into an array and resolved them all at once:
const dirs = await fs.readdir(config.path);
for (let dir of dirs) {
let dirFullPath = `${config.path}${dir}`;
const projectDir = await fs.readdir(dirFullPath);
if (projectDir.includes("package.json")) {
projects.push(getAuditPromise(dirFullPath, dir, spinner));
}
}
projectsOutputs = await Promise.all(projects);
Now all the npm audit calls kick off at the same time. Way faster, way smoother.
The thing I learned: await inside a loop is usually bad. If the tasks don’t depend on each other.
But then I learned about Promise.allSettled
After getting this working I realized there’s a problem. If one project has a corrupted package.json and npm audit throws, Promise.all cancels everything. You lose results from all the other projects.
Promise.allSettled waits for every promise to finish. Each result tells you whether it succeeded or failed:
const results = await Promise.allSettled(projects);
for (const result of results) {
if (result.status === "fulfilled") {
projectAudits.push(result.value);
} else {
spinner.warn(result.reason);
}
}
One thing that caught me out. Your promises need to actually reject on failure, otherwise everything shows as fulfilled even when something went wrong. I had my function resolving bad output and allSettled had nothing to catch. Took me a minute to figure out why it wasn’t working.
For a vulnerability scanner this is probably more correct. If a project fails I can still show the user which ones succeeded and warn them that a specific project might have a corrupted lock file.
Thats it
Have a great Day 🙂
Soy Senior Architect y rechazo el 90% de los CVs Junior por los mismos 3 errores. (La realidad del mercado en 2026).
Sé que hay mucha frustración ahora mismo. Veo a gente enviando cientos de currículums por LinkedIn y recibiendo solo el típico correo automático de “hemos decidido avanzar con otros candidatos”.
Como alguien que hace entrevistas técnicas casi todas las semanas, os quiero dar la perspectiva desde el “otro lado” de la mesa. El problema casi nunca es que no sepáis suficiente código. El problema es que el mercado ha cambiado radicalmente desde el boom de 2021 y la mayoría de Bootcamps siguen enseñando con tácticas de hace 5 años.
Hoy, la Inteligencia Artificial (Cursor, Copilot, Claude) pica el código repetitivo en segundos. Las empresas ya no contratan “manos” para centrar divs; contratan resolutores de problemas.
Si no te llaman, probablemente estés cometiendo uno de estos 3 errores de empleabilidad:
-
El síndrome del “Clon” en el Portfolio Si tienes un clon de Netflix, una App del clima, un e-commerce de tutorial de Udemy o una To-Do list… para un reclutador eres invisible. Ese portfolio grita: “Sé copiar un tutorial paso a paso, pero no sé pensar arquitecturas”. Solución: Borra la basura. Deja 1 o 2 proyectos pero que resuelvan un problema real, a ser posible integrando alguna API de IA (un RAG, un automatizador, etc.).
-
Ser un “Entusiasta de la Tecnología” genérico Nadie contrata genéricos. Si tu CV dice “Fullstack Developer Junior”, vas al mismo saco que otros 1.000. Solución: Posiciónate en un nicho que haga falta. “Frontend React especializado en rendimiento e integraciones IA” suena infinitamente mejor y más resolutivo.
-
Fallar en la comunicación pura Las entrevistas técnicas de junior son un 80% comunicación y un 20% código. Si te pido que me expliques el bug más difícil que arreglaste la semana pasada y tardas 4 minutos en dar vueltas sin ir al grano, me estás diciendo que vas a hacer perder el tiempo a los Seniors de mi equipo.
¿Cómo saber si estás enfocado o si vas al abismo?
Viendo este panorama, he programado una herramienta gratuita rápida para la comunidad. Es un Test de 5 Puntos que mide tu empleabilidad real técnica y estratégica hoy mismo.
No es el típico test de preguntas de sintaxis, es sobre tu método. Respondes 5 preguntas clave, calcula tu puntuación, te dice en qué zona estás (verde, naranja, gris o roja) y te da el plan de acción exacto para tu caso.
Os lo dejo por aquí por si a alguno le sirve para recalcular su ruta antes de seguir quemando meses a lo tonto: 👉 https://zero2dev.es/test
Si tenéis dudas sobre vuestro stack o portfolio actual, dejádmelas en los comentarios y os doy algo de feedback sincero cuando salga de trabajar. ¡Ánimo a todos!
How We Ship Production-Ready Software in 14 Days (Not MVPs)
For about fifteen years, the software world has used “MVP” as an acceptable excuse for shipping broken things.
A login page that doesn’t work on mobile. A payment flow that fails on the third edge case. A dashboard with no error handling. “It’s an MVP” became the universal cover for incomplete engineering.
At our company, we stopped accepting that framing. Here’s what we replaced it with — and the actual engineering workflow that makes it possible.
The MVP Was a Compromise With Slow Tooling
The original MVP idea made sense in 2010. Shipping was slow. Building anything took months. So you shipped the smallest possible thing, learned from real users, iterated.
But “minimum” became a permission slip. Teams started shipping things that weren’t viable in any reasonable sense — they just barely compiled.
By 2026, the tooling has caught up. Senior engineers paired with the right AI tools can now produce in two weeks what used to take six months. The compromise that made MVPs necessary no longer exists.
So we stopped shipping them.
What “Finished Product” Actually Means in Our Workflow
When a founder describes an idea to us, we don’t ask “what’s the smallest version?” We ask “what does the production version look like?” Then we build that.
For us, “production-ready” is a defined checklist. It must have:
- Authentication that works on every supported device
- Payment integration tested against real Stripe events (or whatever the chosen processor is)
- Mobile-responsive layouts at every breakpoint
- Error states for every API call, not just the happy path
- Database schema designed for the next 6 months, not the demo
- A test suite that covers the critical paths
- A deployment pipeline that doesn’t depend on one person’s laptop
- Documentation that another engineer can read and contribute against
If a feature ships without those, it isn’t production-ready. We don’t call it “v1” or “MVP.” We treat it as incomplete and finish it.
The Engineering Workflow
Here’s the actual loop we run, condensed:
Day 1-2 — Scope lock
A senior engineer sits with the founder. The output is a written scope document, signed off by both sides. Anything not in this scope doesn’t get built in this cycle. This is the single biggest predictor of whether the 14-day timeline holds.
Day 3-4 — Architecture + skeleton
Database schema, route map, third-party integrations decided. The skeleton of the codebase exists — empty endpoints, empty components, but the shape is there. AI tools accelerate this phase heavily because the structure is well-understood.
Day 5-10 — Build
This is where AI + senior engineer pairing pays off most. The engineer makes architectural decisions, names things well, reviews edge cases, and writes the tricky parts. The AI fills in boilerplate, generates the obvious stuff, and handles repetitive scaffolding. The engineer reviews every commit.
The ratio matters: AI doing 100% of the work produces fragile code. AI doing 0% of the work means we ship in 6 months. The right ratio — variable per task, but roughly 60/40 engineer-driving / AI-assisting — is where the speed comes from.
Day 11-12 — Hardening
Edge cases, error handling, mobile testing, accessibility check, security pass. This is where MVPs cut corners and we don’t.
Day 13 — Staging + founder review
Everything deployed to staging. Founder gets a real walkthrough on real devices. Bugs filed.
Day 14 — Ship
Production deployment. Source code transfer to the founder’s GitHub. Documentation handed over.
That’s the loop. Nothing exotic — just a disciplined refusal to ship things that aren’t finished.
What AI Actually Does (And Doesn’t Do)
The “AI replaces engineers” framing is wrong. AI does not:
- Make architectural decisions
- Review its own security
- Understand the business
- Catch the edge case that only matters because of how this specific customer uses the product
- Decide what to not build
What AI does well in this workflow:
- Generate boilerplate (forms, CRUD, schema mapping)
- Translate well-defined intent into code quickly
- Handle obvious refactors
- Write the first draft of tests
- Catch syntax errors and obvious bugs
The engineer’s job becomes increasingly about judgment — what to build, in what order, what to refuse, what to harden, what is the obvious-but-wrong shortcut. The typing is faster. The thinking is the same.
Why This Works Economically
If our team can ship a production-ready product in 14 days instead of 6 months, the unit economics change completely.
That’s why our company can do something unusual: the first project we build with any founder is free. Not a stripped-down trial, not a “lite” version — the same full, production-ready product I described above, with the source code transferred to them at the end.
We can do this because the cost per project has collapsed. We do it because we’d rather show a founder our work than promise it. If they come back for a second project, the relationship is real. If they don’t, they walk away with a product they own.
That’s it. That’s the company.
What This Means If You’re Considering Shipping Something
If you’ve been sitting on an idea because every option in front of you came with a contract, a credit card, a six-month wait, or a $50K invoice — the economics of software have changed enough that those options aren’t your only ones.
A team that ships finished products in 14 days exists. The first one can be free. The source code can be 100% yours.
If you want to see what that looks like for your idea, we’re at
fluxez.com.
Galileo Gosling the father of Java
Before Getting into development straight away.
We must learn the origin the customs and the way of Java Right from the start.
We must start first with the man behind it.
The Father of Java James Gosling was born in 1955 in Canada and later Joined Sun Microsystems in 1984 where he worked for 26 years.
In 1994 He got the Idea while writing a program to port software from a PERQ by translating Perq Q-Code to VAX assembler you know just normal stuff
He created the original design of Java and implemented the compiler (JAVAC) and also the Virtual machine (JVM)
Though it wasn’t all sunshine. Java was acquired later on by Oracle from sun.
After this James Gosling decided to part ways with sun microsystems in April 2 2010. Since then he has taken a critical stance toward Oracle.
In march 2011 he joined Google for six months.
After he followed his friend Bill Vass and joined into a startup Liquid Robotics. Things were doing better for Gosling until the startup became too good, later in 2016 Liquid Robotics was acquired by Boeing.
Following the inevitable Mr Gosling left Liquid Robotics and joined AWS in 2017 as a Distinguished Engineer and later Retired in 2024
Being A creator comes with a lot of hardships and mishaps. Even though Gosling had differences with oracle because of being stripped off decision making authority. People still associate him Greatly with the JAVA language He is still the father of Java
Aside from all this he was still accredited for a lot of things
In
- 2002: awarded The Economist Innovation Award
-
2002: awarded The Flame Award USENIX Lifetime Achievement Award.
-
2007: made an Officer of the Order of Canada.[32] The Order is Canada’s second highest civilian honor. Officers are the second highest grade within the Order.
-
2013: became a fellow of the Association for Computing Machinery.
-
2015: awarded IEEE John von Neumann Medal
-
2019: named a Computer History Museum Fellow for the conception, design, and implementation of the Java programming language
Even with all the challenges Gosling remains an inspiration to many for his resilience including myself
Why Most Developers Burn Out (And How I Almost Did Too)
🚀 Why Most Developers Burn Out (And How I Almost Did Too)
“You don’t burn out because you work too hard. You burn out because you work without meaning.”
I didn’t believe that until I hit a point where even opening my code editor felt heavy.
This is not a productivity guide. It’s a reflection on what actually breaks developers.
🧠 The Illusion of Productivity
At some point, I thought productivity looked like this:
- Writing more code
- Fixing more bugs
- Staying online longer
- Saying “yes” to everything
But here’s the truth:
More output ≠ more progress
I once spent 14 hours straight debugging something that turned out to be a missing comma.
Yes. A comma.
⚙️ The System That Traps You
Most dev burnout doesn’t come from coding itself.
It comes from:
1. Constant Context Switching
Jumping between:
- Slack messages
- Jira tickets
- GitHub PRs
- Meetings that could’ve been messages
2. Invisible Deadlines
No one says it out loud, but you feel it:
“This needs to be done soon…”
3. Always-On Culture
Even when you’re “resting”, your brain is still here:
while (alive) {
thoughts.push("Did I break production?");
}
💥 The Breaking Point
I remember this one night.
I had:
- 3 tabs of Stack Overflow
- 2 failing builds
- 1 very angry PR comment
- 0 clarity
And I just stared at the screen.
Not tired.
Just… disconnected.
That’s the scary part. Not exhaustion — numbness.
🧭 What Actually Helped Me
Not motivation. Not hacks.
Just small resets.
🪶 1. Writing things down before coding
Instead of jumping into code:
## Plan
- What problem am I solving?
- What is the smallest possible fix?
- What can I ignore?
🧱 2. Reducing “work in progress”
I stopped doing this:
Task A + Task B + Task C (all half done)
And started doing:
Task A → done
Task B → done
Task C → done
🧘 3. Accepting “good enough”
Not everything needs to be perfect.
Even React components.
Yes, I said it.
📊 A Simple Truth
Here’s what changed everything:
| Old Thinking | New Thinking |
|---|---|
| Work more | Work focused |
| Optimize everything | Optimize nothing early |
| Say yes | Say “not now” |
| Rush output | Respect attention |
🧪 A Tiny Experiment You Can Try
For the next 3 days:
Before starting any task, write one sentence only:
“What does done look like?”
That’s it.
No planning boards. No overengineering.
Just clarity.
🔥 Final Thought
Burnout doesn’t happen suddenly.
It builds quietly:
- One “quick fix”
- One “extra task”
- One “I’ll do it later”
- One “sure, I can handle it”
Until you can’t.
And the fix isn’t dramatic.
It’s just less noise, more intention.
If you're tired, you're not weak.
You're overloaded.
📌 If this resonated
You don’t need a reset button.
You need fewer tabs open in your life.
Building Privacy-First Browser Tools: Why Client-Side Processing Wins
Most online tools follow the same architecture: user uploads file, server processes it, server returns
result. This is the default. It’s also wrong for most use cases.
When I started building Instant Tools Hub, I made one architectural decision early: everything runs
client-side. No file uploads to servers. No backend processing. No user data leaving the browser.
This post explains why, and how.
The Problem with Server-Side Tools
Server-side processing has real costs:
Privacy: Every file you upload is, by definition, accessible to the operator. OWASP has long
flagged third-party file upload as a major privacy risk. For sensitive documents (resumes, contracts,
ID scans, medical files), this is a problem most users don’t fully consider.
Infrastructure cost: Every PDF merge or image compression burns server CPU. For a free tool,
this scales linearly with users, which forces ads, paywalls, or shutdowns.
Latency: Upload, process, download. Three network round-trips for a task that could happen in
200ms locally. Web.dev research shows that latency above 1 second drops user engagement
sharply.
Scalability: A viral spike kills server-side tools. The same spike has zero impact on a client-side
tool.
Modern Browser APIs Are Underrated
Most developers I talk to are still surprised by what browsers can do natively. A quick tour:
Canvas API: Full image manipulation. Resize, compress, format conversion, watermarking.
Combined with toBlob(), you can build a professional-grade image compressor like the one I built in
a few hundred lines of JavaScript.
File API and Blob: Read files from disk, manipulate binary data, generate downloads. No server
needed.
PDF-lib: Pure JavaScript PDF manipulation. Merge, split, modify, watermark — all in the browser.
The library is around 300KB minified, which loads once and caches.
Web Workers: For heavy processing (large PDFs, batch image compression), offload to a worker
thread. Your UI stays responsive.
WebAssembly: For computationally intensive tasks, compile C/C++/Rust to WASM and run
near-native speed in the browser.
Architecture Pattern
A typical client-side tool I build follows this pattern:
- User selects file via input or drag-drop
- FileReader reads it into memory
- Processing library handles the transformation
- Result is written to a Blob
- URL.createObjectURL() creates a download link
- User downloads, blob is revoked
Total network calls: zero (after initial page load). Total server cost: hosting static files. You can see
this in action on every tool at Instant Tools Hub.
Caveats and Honesty
Client-side isn’t free of tradeoffs:
Bundle size matters: Loading a 5MB JavaScript library to compress one image is bad UX.
Lazy-load libraries only when needed.
Mobile constraints: Phones have less RAM and weaker CPUs. Large PDFs (500+ pages) can
crash mobile browsers. Plan for graceful degradation.
Some tasks genuinely need servers: OCR with large models, AI image upscaling, anything
requiring proprietary data. Be honest with users when this is the case.
Why This Matters
The web is increasingly centralized. Five companies operate most of the cloud. Every tool you use
likely runs on infrastructure owned by AWS, GCP, or Azure. Every file you upload becomes
someone else’s problem — and sometimes, their asset.
Client-side tools push back against this. They put computation where it belongs: on the user’s
device, under the user’s control.
If you’re building a productivity tool and your first instinct is to spin up a backend, pause. Ask: does
this actually need a server? Most of the time, the answer is no.
Check out Instant Tools Hub for examples of what’s possible. Or build your own. The web is more
capable than you think.
What client-side tools have you built? Drop a link below — I’d love to see what others are doing in
this space.
— End of Blog Post —
Source: Instant Tools Hub (https://instanttoolshub.in/)[](url)
HookGuard AI: A Local React Hook Reviewer Powered by Gemma 4 and Ollama
This is a submission for the Gemma 4 Challenge: Build with Gemma 4
What I Built
HookGuard AI is a local AI-powered code review tool focused on React hooks.
The goal is simple: help developers catch risky hook patterns before they reach production or even human code review.
React hooks are powerful, but small mistakes can create subtle bugs: unstable dependencies, unnecessary effects, stale closures, infinite render loops, or hard-to-maintain component logic.
HookGuard AI analyzes React components and returns a practical review with explanations, severity levels, and suggested improvements.
The project is designed to be local-first. Source code stays on the developer’s machine, and the AI review is powered by Gemma 4 through Ollama.
Demo
Live demo:
https://hookguard-ai.vercel.app
The current workflow is:
- Paste or upload a React component.
- Run the HookGuard AI review.
- Get structured feedback about hook-related issues.
- Review explanations and suggested fixes.
- Use the output as a pre-review step before opening a pull request.
The tool focuses on practical developer feedback, not generic AI comments. The review is meant to help answer questions like:
- Is this
useEffectreally needed? - Can this dependency array create problems?
- Is this callback unstable?
- Could this state update cause a render loop?
- Is this hook logic harder to maintain than it needs to be?
Code
GitHub repository:
https://github.com/Arrayo/hookguard-ai/
The project includes a React frontend, a Node.js backend, static analysis around React hook usage, and a Gemma-powered review layer.
How I Used Gemma 4
Gemma 4 is the reasoning layer behind HookGuard AI.
The project does not use the model as a generic chatbot. Instead, Gemma 4 receives focused context about the submitted React component and generates a structured code review.
I chose Gemma 4 E4B because the project needs a good balance between local execution and useful reasoning.
For this use case, the model does not need to understand an entire large codebase at once. It needs to reason well about a specific React component, its hooks, its dependencies, and the possible consequences of its implementation.
That makes E4B a good fit:
- small enough for a local-first workflow
- strong enough for focused code reasoning
- practical for fast iteration during development
- aligned with the privacy goal of not sending source code to a hosted API
HookGuard AI combines static analysis with model reasoning.
Static analysis is useful for extracting facts from the code, but it can be too rigid on its own. Gemma 4 helps turn those facts into developer-friendly explanations and practical refactoring suggestions.
For example, instead of only saying “dependency issue detected”, the model can explain why the dependency is risky, what behavior it may cause, and how the developer could fix it.
This hybrid approach keeps the review more focused than a plain prompt over raw source code.
Why This Matters
A lot of AI developer tools try to do everything.
I wanted to build something narrower: a tool that focuses on one painful area of frontend development and tries to do it well.
React hook bugs are common because they often look correct at first glance. The code compiles, the UI may work in simple cases, and the real issue appears later as a performance problem, stale data, or a confusing re-render.
HookGuard AI is my attempt to make those issues easier to catch earlier.
The local-first approach also matters. Developers often work with private repositories, internal business logic, and sensitive source code. Running the review locally with Gemma 4 and Ollama makes the workflow more comfortable for real-world usage.
Technical Notes
The project is built around a simple flow:
- The user submits React code.
- The backend analyzes the code structure.
- Relevant hook-related context is extracted.
- Gemma 4 receives the focused review context.
- The frontend displays the AI review in a developer-friendly format.
The important design decision was not to rely only on raw prompting.
A model can review code directly, but the results are better when the input is scoped and structured. By combining static analysis with Gemma 4, HookGuard AI can produce feedback that is more specific to React hooks and less likely to become vague.
Challenges
The hardest part was keeping the AI feedback useful.
Generic AI code reviews can sound confident while still being too broad. For this project, I wanted the output to be practical: clear issue, reason, severity, and suggested fix.
Another challenge was deciding what the model should do and what regular code should do.
My conclusion was:
- deterministic code should extract facts
- Gemma 4 should reason about those facts
- the UI should make the result easy to act on
That separation makes the project easier to maintain and easier to improve.
What I Learned
The biggest lesson from building HookGuard AI is that local AI tools become much more useful when they are focused.
A general assistant is helpful, but a focused tool can fit better into a developer workflow.
For me, the most promising direction is not replacing linters, tests, or code reviews. It is adding a reasoning layer on top of them.
Gemma 4 works well in this kind of setup because it can explain the “why” behind a potential issue, not just detect a pattern.
What’s Next
Some improvements I would like to add next:
- GitHub pull request integration
- inline code suggestions
- more React-specific review rules
- configurable severity levels
- better false-positive detection
- support for larger multi-file reviews
- comparison between different Gemma 4 model sizes
Final Thoughts
HookGuard AI is a small experiment, but it reflects how I think AI developer tools should evolve.
They should be practical, focused, privacy-aware, and integrated into real workflows.
Gemma 4 makes that kind of local AI tooling much more realistic.
ReactJS Facts & Tricks That 90% Developers Learn Too Late
Most developers think! React is based on any UI library
Most developers think React becomes powerful because of UI libraries like MUI, Ant Design, or Chakra UI.
But the truth is the opposite.
React becomes truly powerful when you understand how to build scalable UI systems without depending entirely on heavy abstractions. Libraries can speed up development. But they can also hide React’s real capabilities. And many developers, even with 5+ years of experience, never fully discover them.
1. React’s Biggest Power Is Component Composition
Most developers use React like this:
<Button />
<Card />
<Modal />
But React’s real superpower is composition.
Example:
<Modal>
<Modal.Header />
<Modal.Body />
<Modal.Footer />
</Modal>
This pattern allows you to** build your own design systems instead of depending** on external UI libraries. Most developers use components. Very few build component architectures.
2. You Don’t Need MUI to Build Enterprise-Level UI
This surprises many developers.
A properly structured React app with:
- reusable primitives
- composition
- utility classes
- design tokens
- custom hooks
can scale more cleanly than many heavy UI-library-based projects.
**
Why?
**
Because you fully control:
- rendering
- styling
- bundle size
- architecture
- accessibility
- performance
Heavy UI libraries often introduce:
- unnecessary abstractions
- large bundle sizes
- styling conflicts
- difficult overrides
- deep dependency chains
3. Most Developers Never Learn Headless UI Architecture
This is one of React’s hidden powers.
Instead of coupling:
logic + styles + markup
Advanced React systems separate them.
const { open, toggle } = useDropdown()
Now the UI can be anything:
- Tailwind
- CSS Modules
- Styled Components
- Vanilla CSS
- completely custom design systems
> This is called headless architecture. Many senior developers still never learn this pattern deeply.
4. React Components Can Behave Like XML-Based Systems
This is where React becomes extremely powerful.
Example:
<Table>
<Table.Header />
<Table.Body />
<Table.Row />
</Table>
This looks almost like a custom XML language, and that’s the hidden beauty of React. You are not just building pages. You are designing your own UI language. This is how advanced systems and design frameworks are created internally at large companies.
5. Compound Components Are One of React’s Most Advanced Patterns
Most developers never master this.
Example
<Tabs>
<Tabs.List />
<Tabs.Tab />
<Tabs.Panels />
</Tabs>
This Creates:
- clean APIs
- scalable UI systems
- reusable architectures
- flexible component control
MUI internally uses many of these architectural ideas. But most developers use the library without understanding the pattern underneath it.
6. React Is More Powerful Without Over-Abstraction
Many developers become trapped inside UI ecosystems.
Eventually:
- Customization becomes difficult
- Performance becomes harder to optimize
- Styling becomes inconsistent
- Components become dependent on library APIs
But when you deeply understand:
- Composition
- State architecture
- Render control
-
Reusable primitives
You can build systems that are: -
Lighter
-
Faster
-
Cleaner
-
Easier to scale
7. Most Developers Never Build Their Own UI Primitives
This is a huge missed opportunity.
Instead of importing:
<Button />
Advanced React engineers often build:
<BaseButton />
<IconButton />
<PrimaryButton />
<DangerButton />
From small reusable primitives.
This gives:
- Total design control
- Consistency
- Scalability
- Maintainability
8. React’s Real Power Is Separation of Concerns
Most beginners mix:
- UI
- API logic
- Permissions
- Validation
- State management
Inside components. Advanced React systems separate everything.
Example structure:
/features
/auth
auth.api.js
auth.store.js
auth.hooks.js
AuthModal.jsx
This architecture matters far more than which UI library you use.
9. Most React Developers Never Understand Render Control
React is not just rendering components.
It is managing:
- Identity
- Reconciliation
- State preservation
- Tree comparison
- Dependency tracking
Example:
<Component key={id} />
This can completely recreate a component instance.
That single line can:
- Reset forms
- Restart animations
- Clear internal state
- Force fresh lifecycle behavior
Most developers never intentionally use this power.
10. The Best React Developers Build Systems, Not Screens
Junior developers build pages. Senior developers build reusable systems.
Elite React engineers build:
- Design systems
- Component APIs
- Render architectures
- Reusable primitives
- Scalable frontend ecosystems
And that is where React becomes more powerful than most developers ever realize.
Final Thought
Most developers use React to consume UI libraries. Very few learn how React itself can become the UI system.
That is the hidden difference between using React…
and mastering it.
A sticky parallax hero in twenty lines of JS
Where the idea came from
A few weeks ago someone asked on a forum how to recreate the Mammut hero — the one where a full-screen image pins to the viewport while text scrolls up over it. They called it “a simple, I think, parallax question.”
Kind of! The Mammut hero is the type of effect that looks like one thing and turns out to be three things stacked. GSAP or Locomotive Scroll handle it beautifully, but it’s also doable with position: sticky and twenty lines of JavaScript — handy when you don’t want to pull in a whole library for a single hero.
I haven’t built this on a real client project yet — the thread just made me curious enough to break it down. What follows is the version I’d share if someone asked me how to do it: the same composition Mammut uses, simplified down to the load-bearing primitives, plus a few small bits of production polish to round it out. Plain HTML and CSS, no framework needed. Live demo lives in the lab.
Three layers, not one
The illusion you’re seeing on mammut.com is built from:
- A sticky-positioned container that pins to the viewport.
- An image inside it that translates slightly while pinned — the parallax-within-pin.
- A text block placed after the sticky container in the DOM, which scrolls up at normal speed and passes over the pinned image.
The whole thing is wrapped in an outer block whose height determines how long the hero stays pinned. When that outer block’s bottom hits the viewport bottom, the sticky element releases and the page continues normally.
That structure is the whole trick. Everything else is decoration.
The markup
<section class="hero-block">
<!-- 1. The pinned container -->
<div class="hero-sticky">
<div class="hero-image"><img src="..." alt="..."></div>
<div class="hero-title">
<h1>Senior dev in the room.</h1>
</div>
</div>
<!-- 2. The text that scrolls over -->
<div class="hero-text">
<p>Ivo Jerković is a full-stack developer…</p>
</div>
</section>
The .hero-image and .hero-title are inside the sticky container so they get pinned along with it. The .hero-text is a sibling of .hero-sticky, sitting after it in the DOM. That ordering is what makes the text appear to scroll up over the image: as the user scrolls, the sticky element holds its position, and the text below comes up into view.
The CSS that does the work
.hero-block {
position: relative;
background: #000;
}
.hero-sticky {
position: sticky;
top: 0;
height: 100vh;
overflow: hidden;
}
.hero-image {
position: absolute;
top: -50px;
left: 0;
width: 100%;
height: calc(100% + 300px); /* slack for the translation */
will-change: transform;
}
.hero-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.hero-text {
position: relative;
max-width: 600px;
margin: 0 0 0 auto;
padding: 80vh 48px 80vh; /* this padding is the hero's "duration" */
color: white;
}
Two non-obvious details:
The image needs vertical slack. It gets top: -50px and height: calc(100% + 300px) so that when JS translates it upward by ~20% of the viewport height, no background shows through at the bottom edge. The slack budget has to be greater than the maximum translation distance.
The text padding is the scroll duration. That padding: 80vh 48px 80vh on .hero-text is what gives the hero its length. Make the padding taller and the image stays pinned longer. Make it shorter and the hero releases faster. There’s no “scroll length” property to set — the height of the text block (plus padding) determines everything.
The JS — twenty lines
const block = document.getElementById('hero-block');
const image = document.querySelector('.hero-image');
const title = document.querySelector('.hero-title');
function update() {
const rect = block.getBoundingClientRect();
const vh = window.innerHeight;
// Progress 0..1 across the block.
// 0 = block top just locked to viewport top.
// 1 = block bottom reached viewport bottom (about to release).
const total = block.offsetHeight - vh;
const scrolled = -rect.top;
const progress = Math.max(0, Math.min(1, scrolled / total));
// Translate as a fraction of viewport height, not pixels —
// so the effect scales with screen size.
image.style.transform = `translate3d(0, ${progress * vh * -0.18}px, 0)`;
title.style.transform = `translate3d(0, ${progress * vh * -0.10}px, 0)`;
}
update();
window.addEventListener('scroll', update, { passive: true });
The whole “parallax” effect is one number: progress, a value between 0 and 1 representing how far through the sticky block the user has scrolled. Image moves 18% of viewport height across the whole sticky lifetime; title moves 10% — slightly faster relative motion, giving the depth cue.
That’s the basic demo. It works — and for plenty of use cases, that’s all you need. Three small additions make it production-friendly.
Three things to add before shipping
1. The scroll listener fires too often
update() runs on every single scroll event. On a 120Hz display, that can be 120 calls per second. The math is cheap but the layout reads (getBoundingClientRect, offsetHeight) are not — each one can force a layout if a previous frame queued style changes.
Wrap it in requestAnimationFrame with a ticking flag:
let ticking = false;
function onScroll() {
if (ticking) return;
ticking = true;
requestAnimationFrame(() => {
update();
ticking = false;
});
}
window.addEventListener('scroll', onScroll, { passive: true });
Now update() runs at most once per animation frame. On a 120Hz display that’s still 120fps of smooth animation but you’ve stopped re-doing work between paints.
2. No prefers-reduced-motion handling
Some visitors have their OS set to “reduce motion” — parallax effects can be physically nauseating for them. Honor the preference and skip the translations:
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
function update() {
// …progress math…
const imageY = reduce ? 0 : progress * vh * -0.18;
const titleY = reduce ? 0 : progress * vh * -0.10;
// …
}
The image still pins (that’s a layout thing, not a motion thing) — only the drift is suppressed. The pinned-and-static version still reads correctly as a hero.
3. The listener runs even when the hero is off-screen
Once the user has scrolled past the hero, the update() math keeps running on every scroll for the rest of the page. Wasted work. Gate the listener with an IntersectionObserver:
let inView = false;
const io = new IntersectionObserver(([entry]) => {
inView = entry.isIntersecting;
}, { rootMargin: '50% 0px' });
io.observe(block);
function onScroll() {
if (!inView || ticking) return;
// …
}
The rootMargin: '50% 0px' keeps the observer “active” for half a viewport of slack above and below the hero, so the math fires just before and just after the hero is visible — no flicker.
A few gotchas worth knowing about
iOS Safari and sticky inside transformed parents. If any ancestor of .hero-sticky has a transform, filter, will-change, or perspective other than none, sticky positioning breaks — silently. The element stops pinning. If your hero “kind of works on desktop but does nothing on iPhone,” this is almost always why. Audit the parent chain.
backdrop-filter does the same thing. Burned me on this exact site — the header had backdrop-filter: blur(8px) and a fixed-position drawer inside it was being clipped to the header’s box instead of the viewport. Same root cause: backdrop-filter establishes a containing block for fixed and sticky descendants.
The image-slack math is brittle. If you ever change the max translation in JS (say from 18% to 30% of viewport height), the CSS height: calc(100% + 300px) may no longer be enough slack — 30% of a 1080px desktop viewport is already 324px, over budget. Safer to express the slack as a function of viewport in CSS too: height: calc(100% + 25vh) keeps the two in lockstep.
object-position: center is fine until you have a tall vertical image on a wide landscape viewport, or vice versa. Then your subject ends up cropped weirdly. For hero images that need a specific focal point, set object-position to a fraction (50% 30%) and pick it deliberately per image.
Why I went vanilla here
Most posts about parallax heroes start with an animation library, and that’s a great call when you need timeline orchestration or scroll-driven sequences. GSAP especially is hard to beat once the choreography gets complex. For this specific effect — a hero that pins and drifts — position: sticky and transform are already doing most of the work, so a library would mostly be along for the ride.
Twenty lines of vanilla JS, four CSS rules, and a clear mental model of what each layer is doing. That’s the whole thing. The forum poster who asked the question had it right when they called it “simple” — once the three primitives are named, it really is.
Originally posted at ivojerkovic.com.
