Collection page
This example combines collection stats, an item grid and recent activity. Adapt it to your application's loading, error and refresh behavior. GraveMarket's public reads do not require a credential.
Integration review
With the MCP server installed, ask your agent to "review this file for Solana Deads integration mistakes".
Install
npm install @solanadeads/gravemarket// lib/gravemarket.ts
import { GraveyardClient } from '@solanadeads/gravemarket';
export const gmk = new GraveyardClient();The public client works without a key. See optional API keys for authenticated use.
The page
import { useEffect, useState } from 'react';
import { gmk } from './lib/gravemarket';
// listing_price is NULLABLE and null means NOT LISTED — not free, not zero.
function priceLabel(item) {
return item.listing_price == null
? 'Not listed'
: `${item.listing_price} ${item.listing_currency}`;
}
// Include the chain and token ID for EVM collections.
const itemKey = (i) => `${i.chain}:${i.token_address}:${i.token_id ?? ''}`;
export function CollectionPage({ slug }: { slug: string }) {
const [collection, setCollection] = useState(null);
const [stats, setStats] = useState(null);
const [items, setItems] = useState([]);
const [activity, setActivity] = useState([]);
const [cursor, setCursor] = useState(undefined);
const [loading, setLoading] = useState(false);
useEffect(() => {
gmk.collections.get(slug).then(setCollection);
gmk.collections.stats(slug).then(setStats);
gmk.collections.activity(slug, { limit: 10 }).then((r) => setActivity(r.data));
loadMore(true);
}, [slug]);
async function loadMore(reset = false) {
setLoading(true);
const page = await gmk.collections.items(slug, {
limit: 24,
...(reset ? {} : { cursor }),
});
setItems((prev) => (reset ? page.data : [...prev, ...page.data]));
setCursor(page.cursor); // null/undefined when exhausted
setLoading(false);
}
if (!collection) return <p>Loading…</p>;
return (
<div>
<header>
<h1>{collection.name}</h1>
{/* Floor currency varies BY CHAIN — always render it alongside the number. */}
<p>
Floor: {collection.floor_price ?? '—'} {collection.floor_currency ?? ''}
{' · '}{collection.chain}
</p>
{stats?.wash_trade_count_7d > 0 && (
<p>
Volume figures include {stats.wash_trade_count_7d} suspected wash
trade(s) in the last 7 days.
</p>
)}
</header>
<ul>
{items.map((item) => (
<li key={itemKey(item)}>
<img src={item.thumbnail_url ?? item.image_url} alt={item.name ?? ''} />
<span>{item.name ?? item.token_address.slice(0, 8)}</span>
<span>{priceLabel(item)}</span>
{item.rarity_rank && <span>Rank #{item.rarity_rank}</span>}
</li>
))}
</ul>
{/* `cursor` present means more pages. Do not compute totals from what is loaded. */}
{cursor && (
<button onClick={() => loadMore()} disabled={loading}>
{loading ? 'Loading…' : 'Load more'}
</button>
)}
<section>
<h2>Recent activity</h2>
{activity.map((e, i) => (
<div key={i}>{e.event_type} — {e.price ?? '—'} {e.currency ?? ''}</div>
))}
</section>
</div>
);
}Adapting the example
Add request cancellation or stale-response guards when the selected collection changes, and display request failures with a retry action. This example fetches recent activity once; poll or refresh it if your UI needs continuing updates.
Use the collection record for total supply, preserve currency labels and show “Not listed” for a missing listing price. An item key must account for the chain and, on EVM, the token ID.
Draining every page
For a full activity export, follow all pages. The SDK provides async generators:
const events = [];
for await (const batch of gmk.collections.activityAll(slug)) events.push(...batch);For holders, follow the cursor explicitly:
const holders = [];
let cursor;
do {
const page = await gmk.collections.holders(slug, { limit: 100, cursor });
holders.push(...page.holders);
cursor = page.cursor;
} while (cursor);The holders response uses holders for its list field. Check each endpoint's response type when adding pagination.
Next
- The marketplace model — identifiers, prices and pagination
- GraveMarket v1 API — request builders and response examples
- React mint panel — the GraveMint side, if you also render a mint
