Skip to content

Commit bc7f380

Browse files
author
dasathyakuma
committed
minor thingies
1 parent 6e0a456 commit bc7f380

File tree

3 files changed

+146
-57
lines changed

3 files changed

+146
-57
lines changed

examples/marko/fixed/src/routes/+page.marko

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
<let/mounted = false/>
3030
<script>
31-
// @ts-expect-error — Marko LS types <let> as read-only; assignment is valid Marko 6
3231
mounted = true
3332
</script>
3433

packages/marko-virtual/README.md

Lines changed: 145 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,192 @@
11
# @tanstack/marko-virtual
22

3-
Marko 6 adapter for [TanStack Virtual](https://tanstack.com/virtual). Provides
4-
row, column, and grid virtualisation via two auto-discovered Marko tags.
3+
Headless UI for virtualizing scrollable elements in [Marko 6](https://markojs.com), built on top of [`@tanstack/virtual-core`](https://tanstack.com/virtual).
4+
5+
Part of the [TanStack Virtual](https://tanstack.com/virtual) family.
56

67
## Installation
78

8-
```sh
9+
```bash
910
npm install @tanstack/marko-virtual
11+
# or
12+
pnpm add @tanstack/marko-virtual
1013
```
1114

12-
Tags are auto-discovered by the Marko compiler — no imports needed in your
13-
`.marko` files. Just use `<virtualizer>` or `<window-virtualizer>` directly.
15+
**Peer dependency:** `marko >= 6.0.0`
16+
17+
## Setup
18+
19+
Add to your project's `marko.json` to make the tags available:
20+
21+
```json
22+
{
23+
"taglib-imports": ["@tanstack/marko-virtual/marko.json"]
24+
}
25+
```
1426

15-
## Quick start
27+
## Usage
28+
29+
### `<virtualizer>` — scrollable container
30+
31+
Use when you control the scroll element (a div with `overflow: auto`).
1632

1733
```marko
34+
<!DOCTYPE html>
35+
<html>
36+
<body>
37+
1838
<let/mounted = false/>
1939
<script() { mounted = true }/>
2040
21-
<if=mounted>
22-
<div/$scrollEl style="height: 400px; overflow-y: auto">
41+
<div/scrollEl style="height: 400px; overflow-y: auto">
42+
<if=mounted>
2343
<virtualizer|{ virtualItems, totalSize }|
2444
count=10000
2545
estimateSize=() => 35
2646
getScrollElement=scrollEl
2747
>
2848
<div style=`height: ${totalSize}px; position: relative`>
2949
<for|item| of=virtualItems>
30-
<div style=`
31-
position: absolute; top: 0; left: 0; width: 100%;
32-
height: ${item.size}px;
33-
transform: translateY(${item.start}px);
34-
`>
50+
<div
51+
data-index=item.index
52+
style=`position: absolute; transform: translateY(${item.start}px);
53+
height: ${item.size}px; width: 100%`
54+
>
3555
Row ${item.index}
3656
</div>
3757
</for>
3858
</div>
3959
</virtualizer>
60+
</if>
61+
</div>
62+
63+
</body>
64+
</html>
65+
```
66+
67+
> **Why `mounted`?** The virtualizer measures the scroll element's dimensions on mount. During SSR the DOM has no layout, so `offsetHeight` is 0 and no items would be calculated. The `<if=mounted>` guard ensures the virtualizer only renders after the browser has real dimensions available.
68+
69+
### `<window-virtualizer>` — full-page scrolling
70+
71+
Use when the page itself is the scroll container.
72+
73+
```marko
74+
<!DOCTYPE html>
75+
<html>
76+
<body>
77+
78+
<window-virtualizer|{ virtualItems, totalSize }| count=10000 estimateSize=() => 35>
79+
<div style=`height: ${totalSize}px; position: relative`>
80+
<for|item| of=virtualItems>
81+
<div
82+
data-index=item.index
83+
style=`position: absolute; transform: translateY(${item.start}px);
84+
height: ${item.size}px; width: 100%`
85+
>
86+
Row ${item.index}
87+
</div>
88+
</for>
4089
</div>
41-
</if>
90+
</window-virtualizer>
91+
92+
</body>
93+
</html>
4294
```
4395

44-
## Tags
96+
## Tag parameters
4597

46-
### `<virtualizer>`
98+
Both tags use Marko 6's tag parameters pattern. The body receives virtual state via `|{ ... }|` destructuring:
4799

48-
Element-based virtualisation. Covers:
100+
```marko
101+
<virtualizer|{ virtualItems, totalSize, measureElement, scrollToIndex, scrollToOffset }|
102+
count=...
103+
getScrollElement=...
104+
>
105+
<!-- virtualItems, totalSize etc are in scope here -->
106+
</virtualizer>
107+
```
49108

50-
- **Rows** — default (`horizontal` not set)
51-
- **Columns**`horizontal=true`
52-
- **Grid** — compose two `<virtualizer>` tags sharing the same scroll element
109+
## API Reference
110+
111+
### `<virtualizer>`
53112

54-
| Prop | Type | Default | Description |
113+
| Attribute | Type | Required | Description |
55114
|---|---|---|---|
56-
| `count` | `number` | required | Number of items |
57-
| `getScrollElement` | `() => Element \| null` | required | Scroll container |
58-
| `estimateSize` | `(index: number) => number` | `() => 50` | Estimated item size in px |
59-
| `overscan` | `number` | `5` | Items to render beyond the visible area |
60-
| `horizontal` | `boolean` | `false` | Virtualise horizontally |
61-
| `paddingStart` | `number` || Padding before first item |
62-
| `paddingEnd` | `number` || Padding after last item |
63-
| `gap` | `number` || Gap between items |
64-
| `lanes` | `number` | `1` | Lanes for masonry layouts |
65-
| `initialOffset` | `number \| (() => number)` || Initial scroll offset |
115+
| `count` | `number` || Total number of items |
116+
| `getScrollElement` | `() => Element \| null` || Returns the scroll container |
117+
| `estimateSize` | `(index: number) => number` | | Estimated item size in px (default: `50`) |
118+
| `overscan` | `number` | | Items to render outside the viewport (default: `5`) |
119+
| `horizontal` | `boolean` | | Enable horizontal scrolling (default: `false`) |
120+
| `paddingStart` | `number` | | Padding before the first item in px |
121+
| `paddingEnd` | `number` | | Padding after the last item in px |
122+
| `scrollPaddingStart` | `number` | | Scroll padding at the start |
123+
| `scrollPaddingEnd` | `number` | | Scroll padding at the end |
124+
| `gap` | `number` | | Gap between items in px |
125+
| `lanes` | `number` | | Number of lanes for grid layouts |
126+
| `initialOffset` | `number \| (() => number)` | | Initial scroll offset |
127+
128+
**Tag parameters provided to body:**
129+
130+
| Parameter | Type | Description |
131+
|---|---|---|
132+
| `virtualItems` | `VirtualItem[]` | Items to render, with `index`, `start`, `size`, `key` |
133+
| `totalSize` | `number` | Total scrollable size in px — set on the inner container |
134+
| `measureElement` | `(el: Element \| null) => void` | Pass to `ref` for dynamic size measurement |
135+
| `scrollToIndex` | `(index: number, options?: ScrollToOptions) => void` | Scroll to an item by index |
136+
| `scrollToOffset` | `(offset: number, options?: ScrollToOptions) => void` | Scroll to a px offset |
66137

67138
### `<window-virtualizer>`
68139

69-
Window-based virtualisation — the entire page scrolls. Same props as
70-
`<virtualizer>` except `getScrollElement` and `horizontal` are not accepted.
140+
Same as `<virtualizer>` except there is no `getScrollElement`, `horizontal`, or `initialOffset` — the window is always the scroll container.
71141

72-
### Tag variable
142+
## Examples
73143

74-
Both tags expose the same shape via `<tag|{ ... }|>`:
144+
All examples use `@marko/run`. Run any with:
75145

76-
| Property | Type | Description |
77-
|---|---|---|
78-
| `virtualItems` | `VirtualItem[]` | Currently visible items |
79-
| `totalSize` | `number` | Total scrollable size in px |
80-
| `measureElement` | `(el: Element \| null) => void` | For dynamic/variable sizes |
81-
| `scrollToIndex` | `(index: number, options?) => void` | Scroll to item by index |
82-
| `scrollToOffset` | `(offset: number, options?) => void` | Scroll to pixel offset |
146+
```bash
147+
pnpm --filter tanstack-marko-virtual-example-<name> dev
148+
```
83149

84-
## SSR
150+
| Example | Description |
151+
|---|---|
152+
| `fixed` | Fixed-size rows, columns, and grid |
153+
| `variable` | Variable sizes via `estimateSize` |
154+
| `dynamic` | Unknown sizes measured via `measureElement` |
155+
| `grid` | Two virtualizers sharing one scroll element |
156+
| `smooth-scroll` | `scrollToIndex` with CSS smooth scrolling |
157+
| `infinite-scroll` | Lazy data loading with a fixed total count |
158+
| `window` | Full-page scrolling with `<window-virtualizer>` |
85159

86-
`<virtualizer>` and `<window-virtualizer>` are client-only — the
87-
`<lifecycle>` tag inside them never runs during SSR. Always wrap in
88-
`<if=mounted>` where `mounted` is set by `<script>`.
160+
## Dynamic sizing
89161

90-
## Examples
162+
Use `measureElement` to measure items whose size isn't known upfront:
163+
164+
```marko
165+
<virtualizer|{ virtualItems, totalSize, measureElement }|
166+
count=items.length
167+
estimateSize=() => 50
168+
getScrollElement=scrollEl
169+
>
170+
<div style=`height: ${totalSize}px; position: relative`>
171+
<for|item| of=virtualItems>
172+
<div
173+
ref=measureElement
174+
data-index=item.index
175+
style=`position: absolute; transform: translateY(${item.start}px); width: 100%`
176+
>
177+
${items[item.index]}
178+
</div>
179+
</for>
180+
</div>
181+
</virtualizer>
182+
```
183+
184+
> `data-index` is required on measured elements — the virtualizer uses it to map measurements back to items.
185+
186+
## TypeScript
91187

92-
- [Fixed sizes](https://tanstack.com/virtual/latest/docs/framework/marko/examples/fixed)
93-
- [Variable sizes](https://tanstack.com/virtual/latest/docs/framework/marko/examples/variable)
94-
- [Dynamic sizes](https://tanstack.com/virtual/latest/docs/framework/marko/examples/dynamic)
95-
- [Grid](https://tanstack.com/virtual/latest/docs/framework/marko/examples/grid)
96-
- [Smooth scroll](https://tanstack.com/virtual/latest/docs/framework/marko/examples/smooth-scroll)
97-
- [Infinite scroll](https://tanstack.com/virtual/latest/docs/framework/marko/examples/infinite-scroll)
98-
- [Window](https://tanstack.com/virtual/latest/docs/framework/marko/examples/window)
188+
The tags are fully typed. The Marko language server reads the source `.marko` files directly — no `.d.ts` generation is needed. Ensure `@marko/language-tools` is installed in your editor for IDE support.
99189

100190
## License
101191

102-
MIT
192+
MIT © [Tanner Linsley](https://github.com/tannerlinsley)

packages/marko-virtual/marko.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "tags-dir": "./src/tags" }
1+
{ "tags-dir": "./src/tags", "script-lang": "ts" }

0 commit comments

Comments
 (0)