-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Export layout algorithms (unstable) #169
Conversation
Hi there! Can you describe your headless use case and the customizations you want to make? I've been considering re-implementing this library to make it RSC-friendly with zero client-side JS bundle footprint in the next major, and I'm kind of surprised no one has asked for this feature so far. Is that what you are after? |
Exactly that, I want to make an SSR friendly page and currently it's not possible (I don't think..). On different viewports I want to apply different rowConstraints and different defaultContainerWidth, so I was going to do some fiddling and render N different layouts and do some tailwind magic to hook it up :) Our component looks like this, so we kinda have 5 layouts.
<PhotoAlbum
layout="rows"
photos={photos}
spacing={2}
targetRowHeight={targetRowHeight}
defaultContainerWidth={isMobile ? 390 : 1320}
rowConstraints={
photos.length <= 4
? undefined
: containerWidth => {
if (containerWidth < 440) {
return { minPhotos: 1, maxPhotos: 1, singleRowMaxHeight: targetRowHeight }
}
if (containerWidth < 640) {
return { minPhotos: 1, singleRowMaxHeight: targetRowHeight }
}
if (containerWidth < 1024) {
return { minPhotos: 2, singleRowMaxHeight: targetRowHeight }
}
if (containerWidth < 1280) {
return { minPhotos: 3, singleRowMaxHeight: targetRowHeight }
}
return { minPhotos: 4, singleRowMaxHeight: targetRowHeight }
}
}
/> Although the isMobile check isn't very SSR friendly, so we default to isMobile = false for SSR and so on mobile you load in with a different layout then you when the client hydrates it switches to another layout. So I was thinking of doing 5 hooks with all these container widths, and working out the rest myself :p |
Does my use case make sense? Or do you need more info? Sorry if I am being a bit too terse, it's Friday :) |
That's actually a different use case, but I see what you are saying. This library is SSR-compatible in the sense that it renders server-side in an SSR build, but client-side layout shift is difficult to avoid. To achieve zero CLS, you'd need to render N layouts (N Will this approach satisfy your use case? |
Gotcha! I understand that but in our use case this seems like it will bloat up the initial page size. Our gallery can be about 50 images, so that would mean we'd x5 that initial payload to in theory 250 images. So I just wanted to really optimise and this is kind of where this idea came from. The example you sent is really nice although it still relies in window innerwidth which we don't have yet. So that is why I was exploring a CSS powered solution for breakpoints. But perhaps we can edit your shared code and see if we can drop in some tailwind classes :) |
What is your main concern about the page size bloat? An increase in the JS bundle size will be negligible. An increase in the HTML payload size could be up to 5x, but gzip will shave off most of it. I can imagine there may be some wasted bandwidth due to the browser downloading some
You can consider using CSS container queries, as they already have pretty decent support across major browsers. For example, here is what you can build with "use client";
import * as React from "react";
import PhotoAlbum from "react-photo-album";
import photos from "@/app/photos";
const TARGET_ROW_HEIGHT = 220;
const LAYOUTS = [
{ breakpoint: 0, className: "!hidden @[0px]:!flex @[440px]:!hidden" },
{ breakpoint: 440, className: "!hidden @[440px]:!flex @[640px]:!hidden" },
{ breakpoint: 640, className: "!hidden @[640px]:!flex @[1024px]:!hidden" },
{ breakpoint: 1024, className: "!hidden @[1024px]:!flex @[1280px]:!hidden" },
{ breakpoint: 1280, className: "!hidden @[1280px]:!flex" },
];
const BREAKPOINTS = LAYOUTS.map(({ breakpoint }) => breakpoint);
export default function Gallery() {
const [hydrated, setHydrated] = React.useState<number>();
const handleContainerRef = React.useCallback((node: Element | null) => {
setHydrated(node ? BREAKPOINTS.findLastIndex((breakpoint) => breakpoint < node.clientWidth) : undefined);
}, []);
return (
<div ref={handleContainerRef} className="@container">
{LAYOUTS.map(({ breakpoint, className }, index) =>
hydrated === undefined || hydrated === index ? (
<PhotoAlbum
key={index}
layout="rows"
photos={photos}
spacing={2}
breakpoints={BREAKPOINTS}
targetRowHeight={TARGET_ROW_HEIGHT}
componentsProps={hydrated === undefined ? { containerProps: { className } } : undefined}
defaultContainerWidth={index > 0 ? breakpoint : BREAKPOINTS[1] / 2}
rowConstraints={
photos.length > 4
? (containerWidth) => {
if (containerWidth < 440) {
return { maxPhotos: 1 };
}
if (containerWidth < 640) {
return { singleRowMaxHeight: TARGET_ROW_HEIGHT };
}
if (containerWidth < 1024) {
return { minPhotos: 2, singleRowMaxHeight: TARGET_ROW_HEIGHT };
}
if (containerWidth < 1280) {
return { minPhotos: 3, singleRowMaxHeight: TARGET_ROW_HEIGHT };
}
return { minPhotos: 4, singleRowMaxHeight: TARGET_ROW_HEIGHT };
}
: undefined
}
/>
) : null,
)}
</div>
);
} |
Also, if you want to fiddle around with the layout algorithm functions you proposed to export in this PR, you can patch-package this library and see if it allows you to achieve your goal (please share the result!). I will hold off merging this PR for now as I'm not really comfortable exporting layout algorithm functions at this point and making them part of public API. |
On second thought, there is no harm in exporting layout algorithms with the "unstable_" prefix (no semver coverage). I'll go ahead and push the release out. |
🎉 This PR is included in version 2.4.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
contributing guide
PR
Hey! I wanted to kind of use this repo in a headless way, and I thought I can do that by just using the hooks and working around them. It felt easier to customise (or actually, make possible to customise).
So hopefully simply, this feat just exposes some hooks that I can import :)