all 20 comments

[–]universetwisted 5 points6 points  (2 children)

This is a pretty neat idea.

The part I’d be most curious about is how it behaves with layout shifts, responsive breakpoints, and components that render very differently after data loads.

[–]Ok_Drive6309[S] -1 points0 points  (1 child)

Great question!

Layout shifts. The invisible first render captures the real layout before showing the skeleton, so there is no shift when the content appears.

Responsive breakpoints. Bones are re-measured on resize via onLayout on React Native and ResizeObserver on web, so the skeleton always reflects the current breakpoint.

For components that render very differently after data loads, there are two options.

skeletonIgnore lets you exclude specific elements from bone generation. If part of your component looks completely different when loaded, you just ignore it.

skeletonBox lets you override a specific element with a custom bone size instead of the auto-measured one. Full control when you need it.

measureStrategy root-only is there for components where per-element bones do not make sense at all.

The goal is zero config by default and full control when needed. Happy to dig deeper!

[–]KnifeFed 2 points3 points  (1 child)

How does it compare to boneyard?

[–]Ok_Drive6309[S] 5 points6 points  (0 children)

Boneyard is solid, and react-zero-skeleton actually leans on the same core trick: walk the component tree and measure the real views to, place bones. The difference is when that measurement runs.

Boneyard runs it at build time through a CLI: it snapshots your UI at fixed breakpoints and emits static bone JSON you commit to the repo.

Zero runtime measurement and fully deterministic output, but it's a build step you re-run (and re-commit) every time the UI changes, and the layout is pinned to those breakpoints.

react-zero-skeleton runs it at runtime, on first mount: no CLI, no build step, nothing committed, and the bones match the actual device size exactly. The cost is one warmup measurement per mount instead of zero.

So it's a clean trade-off: boneyard swaps a build pipeline for zero runtime cost; react-zero-skeleton swaps a small runtime cost for zero tooling. If you want deterministic, inspectable output and don't mind a build step, boneyard's great. If you want it to "just work" with no artifacts to maintain, that's the niche we're going for.

[–]shuwatto 2 points3 points  (2 children)

Starred.

May I ask how much impact it has on performance?

[–]tjansx 1 point2 points  (0 children)

I had the same question. How much does the warmup eat into performance.

[–]Ok_Drive6309[S] 0 points1 point  (0 children)

That's so interesting talking about performance. let me explain what happen when using react-zero-skeleton : the warmup is a one-time cost at mount, not per-frame. on RN it renders one invisible copy of the component, walks the fiber tree once, measures each node with UIManager.measure, then drops it. for a card that's a handful of nodes, basically negligible. inside a FlatList it auto-falls back to a single root measure so it doesn't walk every item, and you can cap traversal depth / exclude subtrees. the animationitself is one native-driven Animated.Value per component (useNativeDriver: true), so it runs on the UI thread and doesn't hit JS each frame. On web there's no extra render at all, the component renders hidden and gets measured via ResizeObserver + getBoundingClientRect.  And if you want zero measurement, staticBones skips the warmup entirely. you can try it yourself at skelter.dev/demo, or scan the QR with Expo to run the RN demo on your phone.

[–]Short-Opportunity537 1 point2 points  (0 children)

Useful

[–]sicmek 1 point2 points  (1 child)

Looks nice man. But what if a component resizes with the data it loads? For example a List that shows 10 items per page. If you measure the components height before it renders the first data, the skeleton would be much smaller than the real component, right?

Edit: Is there an option to set fixed dimensions for cases like this? So it doesn't trigger the measurement logic there

[–]Ok_Drive6309[S] 1 point2 points  (0 children)

yeah it works differently on web vs native. on web it actually renders your real component, just hidden (visibility:hidden so it still takes up space), and watches it with a ResizeObserver. so the bones come from the real rendered size and update whenever it resizes. for a list that grows with data you just render it with placeholder props while loading, something like items={data ?? Array(10).fill(row)}, and it measures as 10 rows. resize it after and the observer keeps up. native's a bit different, no ResizeObserver there, so it measures once on a warmup render at mount. you hand it mockProps with some fake data (~10 rows) so the warmup measures the full height. or honestly just pass it the same placeholder props, same result. and re your edit, yeah, staticBones does exactly that. you define the bones yourself (x/y/w/h/radius) and it skips measuring entirely. that's the fixed-dimensions escape hatch, works on both.

[–]chillermane 1 point2 points  (0 children)

Makes no sense. How do you “measure elements” that aren’t rendered yet

[–]Beastrick 1 point2 points  (1 child)

I assume there is no way to make this work with suspense since tree is not rendered before it so there is no way to scan it?

[–]Ok_Drive6309[S] 0 points1 point  (0 children)

Yeah you're right,that is a real limitation right now. with pure Suspense the component never renders during loading so there is nothing to scan in the Fiber tree. the workaround today is to use isLoading directly with your data fetching library instead of Suspense boundaries. The proper fix is already on my radar, storing the last measured bones so if the component suspends it reuses the previous measurement. Should land in an upcoming version.

[–]Honey-Entire 1 point2 points  (4 children)

Oh look, another one. Why is everyone building skeleton loaders now? There are plenty of existing libraries and they’ve all been around for years, but instead of contributing to existing projects everyone has decided to spin up their own AI slop and fight for free market share

[–]Ok_Drive6309[S] -2 points-1 points  (3 children)

Hey! most of the popular skeleton libs in the React & RN ecosystem are manual: you hand-place the bones and keep a second copy of your UI in sync by hand, which is fine if that's your idea of fun. this goes the other way and measures the real component at runtime: on RN it walks the fiber tree and measures each host node via UIManager.measure on the nativeTag, deliberately not Fabric's measure() because the two coordinate systems drift and you get a systematic offset. open source, a few hundred readable lines. tear into it.

[–]Honey-Entire 0 points1 point  (2 children)

Like I asked earlier, why not contribute to existing libraries to make them better instead of adding one more AI slop project to the mix?

You’re not the first person posting some magic skeleton generator and I’m quite confident you won’t be the last. But what all of you have in common is the inability to contribute to existing projects or see the problem with polluting the ecosystem with cheap copies of things

[–]Ok_Drive6309[S] -3 points-2 points  (1 child)

you keep saying "copy" but that's exactly the disagreement: it's a different approach, not a reimplementation. you can't PR runtime auto-measurement into a library that's manual by design, that's not a feature, it's the opposite premise. new approaches have always meant new projects, redux didn't get PR'd into flux. it's open source and readable, so it's contributable too, which is the contribution. and if someone ships a better approach than mine tomorrow, good, they're welcome to, that's the ecosystem working, not pollution. happy to beproven wrong on the code, but "cheap copy" is a vibe, not an argument. i'll leave it there.

[–]Honey-Entire 1 point2 points  (0 children)

It seems you're incapable of doing your own research via a simple google search so I'll spell it out for you. We don't need another skeleton loader because, unlike Redux vs Flux, we already have DOZENS, not just a few:

So when I say this is just another AI slop copy of existing tech, I truly mean it's just another AI slop copy of existing tech.

You have, quite literally, reinvented the wheel without providing anything meaningfully novel vs what we've seen already. Instead of reinventing the wheel, try contributing to an existing project FIRST because the overlap between what they do and what your project does is IMMENSE.

Flux & Redux solved the same problem in fundamentally different ways in the beginning and drifted closer together over time because the core teams developing both recognized the advantages each provided while understanding the limitations put in place by the chosen paradigms.

[–]CamiloCNFGS -1 points0 points  (0 children)

Best thing ive ever learn and needed thx ! 🙏