Skip to content

Instantly share code, notes, and snippets.

@delaneyj
Created June 5, 2025 01:56
Show Gist options
  • Save delaneyj/205e51ddb7d5d5e87071ddcf3375d256 to your computer and use it in GitHub Desktop.
Save delaneyj/205e51ddb7d5d5e87071ddcf3375d256 to your computer and use it in GitHub Desktop.
package examples
import (
"github.com/XANi/loremipsum"
"github.com/go-chi/chi/v5"
. "github.com/starfederation/datastar-dev/site/shared"
datastar "github.com/starfederation/datastar/sdk/go"
"math/rand/v2"
"net/http"
"time"
)
func setupProgressiveLOD(examplesRouter chi.Router) {
lorem := loremipsum.New()
examplesRouter.Route("/progressive_lod", func(progressiveLODRouter chi.Router) {
progressiveLODRouter.Get("/", func(w http.ResponseWriter, r *http.Request) {
RenderPage(progressiveLOD(), w, r)
})
progressiveLODRouter.Get("/updates", func(w http.ResponseWriter, r *http.Request) {
sse := datastar.NewSSE(w, r, datastar.WithCompression())
sse.MergeFragmentTempl(progressiveLODEmpty())
components := []templ.Component{
progressiveLODHeaderContent(),
progressiveLODArticleContent("This is my article"),
progressiveLODCommentsContent(),
progressiveLODFooterContent(),
}
rand.Shuffle(len(components), func(i, j int) {
components[i], components[j] = components[j], components[i]
})
for _, c := range components {
sse.MergeFragmentTempl(c)
time.Sleep(500 * time.Millisecond)
}
sse.MergeFragmentTempl(progressiveLODArticleBody(lorem.Paragraphs(2)))
commentCount := rand.IntN(5) + 5
for i := 0; i < commentCount; i++ {
comment := lorem.Sentence()
avatarUrl := "https://avatar.iran.liara.run/username?username=" + lorem.Word()
c := progressiveLODCommentItem(i+1, comment, avatarUrl)
sse.MergeFragmentTempl(
c,
datastar.WithSelectorID("comments-list"),
datastar.WithMergeAppend(),
)
time.Sleep(250 * time.Millisecond)
}
sse.MergeFragmentTempl(progressiveLODLoadButton())
})
})
}
templ progressiveLODArticleBody(content string) {
<section id="articleBody">
<p>
{content}
</p>
</section>
}
templ progressiveLOD() {
@page("progressive_lod") {
@Heading("Explanation")
<p>
This is a response to <a href="https://overreacted.io/progressive-json/">Dan Abramov's article on progressive JSON</a>. I think it's overcomplicated and show a lack of understanding of how powerful native hypermedia is.
</p>
@Subheading("Note")
<p>
This example shows how to progressively load a page using Datastar. The page is divided into sections. We already have examples of <a href="/examples/infinite_scroll">infinite scroll</a> and <a href="/examples/progress_bar">progress bar</a>, but this example shows how to progressively load a page in a more structured way.
</p>
<p>
It's truly baffling to me the amount of complexity that React developers tend to introduces. Hypermedia is a powerful tool that allows you to progressively load content in a way that is simple and efficient. This example shows how to use Datastar's server-sent events (SSE) to progressively load a page in a way that is easy to understand and maintain.
</p>
<p>
Nothing is faster than direct HTML morphing without a virtual DOM, let the browser do the heavy lifting. This example shows how to use Datastar's templating system to progressively load a page in a way that is simple and efficient while only using a one-time cost CDN shim.
</p>
@Demo() {
@progressiveLODDemoContents()
}
}
}
templ progressiveLODDemoContents() {
<style>
#lod {
display: grid;
background-color: var(--gray-1);
grid-template-areas:
"header header"
"article comments"
"footer footer";
grid-template-rows: auto 1fr auto auto;
grid-template-columns: auto 1fr auto;
height: 100%;
border: 1px solid var(--gray-6);
}
#header{
grid-area: header;
background-color: var(--gray-2);
padding: 1rem;
box-shadow: var(--shadow-2);
}
#article {
grid-area: article;
padding: 1rem;
background-color: var(--gray-3);
}
#comments {
grid-area: comments;
padding: 1rem;
background-color: var(--gray-4);
ul {
display: flex;
flex-direction: column;
gap: var(--size-2);
}
li {
font-size: var(--font-size-0);
list-style: none;
}
.avatar {
float:left;
margin-right: var(--size-4);
width: var(--size-8);
aspect-ratio: var(--aspect-square);
border-radius: 50%;
margin-right: 1rem;
}
}
#footer {
grid-area: footer;
background-color: var(--gray-5);
padding: 1rem;
}
</style>
<div>
@progressiveLODLoadButton()
<p>
Each part is loaded randomly and progressively.
</p>
</div>
@progressiveLODEmpty()
}
templ progressiveLODLoadButton() {
<button
id="load-button"
data-signals-load-disabled="false"
data-on-click="$loadDisabled=true; @get('/examples/progressive_lod/updates')"
data-attr-disabled="$loadDisabled"
>
Load
</button>
}
templ progressiveLODEmpty() {
<div id="lod">
<header id="header"></header>
<section id="article"></section>
<section id="comments"></section>
<div id="footer"></div>
</div>
}
templ progressiveLODHeaderContent() {
<header id="header">Welcome to my blog</header>
}
templ progressiveLODArticleContent(title string) {
<section id="article">
<h4>{ title }</h4> // This is my article
<section id="articleBody"></section>
</section>
}
templ progressiveLODCommentsContent() {
<section id="comments">
<h5>Comments</h5>
<p>
This is the comments section. It will also be progressively loaded as you scroll down.
</p>
<ul id="comments-list"></ul>
</section>
}
templ progressiveLODCommentItem(id int, comment, avatarUrl string) {
<li id={ id }>
<img src={ avatarUrl } alt="Avatar" class="avatar" />
{ comment }
</li>
}
templ progressiveLODFooterContent() {
<div id="footer">Hope you like it</div>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment