Improve Lazy Loading, Refactor Filtering via `data-tags` in Toolkit Layout
Summary
Optimized toolkit rendering by switching from tag-based URL filtering to in-page
data-tags filtering, improving responsiveness and removing full page reloads. Also enabled lazy loading and async decoding for tool images to reduce initial load time.Why Care
Previously, selecting a tag triggered a URL reload with query parameters (
?tag=...). This caused the entire page to re-render, resetting scroll position, reloading all cards, and introducing latency. Switching to in-page data-tags filtering enables instant feedback and seamless UX. Lazy loading image assets further improves toolkit performance and lowers bandwidth use.Implementation
Changes Made
🟢 Refactored Toolkit Filtering
- Replaced tag-based URL filtering with in-place DOM filtering using
data-tagson each.tool-card - Updated
CardGrid.astroto ensuredata-tagsis passed in bothToolCardandBareToolCardrenderings - Updated
TagColumn.astroto:- Dispatch custom
tagsUpdatedevent - Filter visible
.tool-cardelements by comparingdata-tagswith selected tags
🟢 Optimized Image Performance
- Enabled
loading="lazy"anddecoding="async"on all<img>tags inToolCard.astro - Preserved existing fallback logic for missing or broken images, with graceful degradation to:
- Screenshot URLs
- Placeholder fallback:
https://i0.wp.com/port2flavors.com/wp-content/uploads/2022/07/placeholder-614.png
Affected Files
text
src/components/tool-components/
├── ToolCard.astro # + lazy loading, data-tag logic
├── TagColumn.astro # + client-side filtering & search
├── BareToolCard.astro # (minor updates, if used)
src/layouts/
├── ToolkitLayout.astro # removed filterTag prop, now passes all tools Technical Details
data-tags Example from ToolCard.astro
astro
<ToolCard
{...tool}
filePath={tool.filePath}
class="tool-card"
data-tags={JSON.stringify(tool.tags || [])}
/> Filtering Logic in TagColumn.astro (JS)
js
function filterCards() {
allCards.forEach(card => {
const tags = JSON.parse(card.dataset.tags || '[]');
const match = selectedTags.every(tag => tags.includes(tag));
card.style.display = match || selectedTags.length === 0 ? '' : 'none';
});
} Removed Prop Logic in ToolkitLayout.astro
astro
// Removed this:
const {
...
filterTag
} = Astro.props;
const filteredEntries = filterTag
? toolEntries.filter(entry => entry.data.tags?.includes(filterTag))
: toolEntries; Now
CardGrid is rendered unfiltered with all entries, and the filtering happens entirely client-side using JavaScript.Integration Points
- Ensures tag filters persist visually and functionally across paginated or dynamically styled content
- Tag filtering now plays well with other layout JS (like mouse tracking and image fallbacks)
- Removed dependency on server-rendered tag filters — no need to sync Astro route logic with query param state
Documentation
No external libraries added or removed. No breaking API changes introduced.