(
did,
"app.bsky.feed.post",
);
if (loading) return Fetching latest post…
;
if (error) return Could not load: {error.message}
;
if (empty || !rkey) return No posts yet.
;
return ;
};
```
The same pattern works for other components: swap the collection NSID and the component you render once you have an `rkey`.
```tsx
const LatestLeafletDocument: React.FC<{ did: string }> = ({ did }) => {
const { rkey } = useLatestRecord(did, "pub.leaflet.document");
return rkey ? (
) : null;
};
```
## Compose your own component
The helpers let you stitch together custom experiences without reimplementing protocol plumbing. The example below pulls a creator’s latest post and renders a minimal summary:
```tsx
import { useLatestRecord, useColorScheme, AtProtoRecord } from "atproto-ui";
import type { FeedPostRecord } from "atproto-ui";
const LatestPostSummary: React.FC<{ did: string }> = ({ did }) => {
const scheme = useColorScheme("system");
const { rkey, loading, error } = useLatestRecord(
did,
"app.bsky.feed.post",
);
if (loading) return Loading…;
if (error || !rkey) return No post yet.;
return (
did={did}
collection="app.bsky.feed.post"
rkey={rkey}
renderer={({ record }) => (
{record?.text ?? "Empty post"}
)}
/>
);
};
```
There is a [demo](https://atproto-ui.wisp.place/) where you can see the components in live action.
## Running the demo locally
```bash
npm install
npm run dev
```
Then open the printed Vite URL and try entering a Bluesky handle to see the components in action.
## Next steps
- Expand renderer coverage (e.g., Grain.social photos).
- Expand documentation with TypeScript API references and theming guidelines.
Contributions and ideas are welcome—feel free to open an issue or PR!