Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Rakkas Tanstack Query Integration

This repo contains an example of using Tanstack Query (formerly known as React Query) with Rakkas.

Open in StackBlitz

How does it work?

In short, we bypass Tanstack Query's own dehydration/hydration mechanism and do the serialization/deserialization ourselves by injecting scripts that call queryClient.setQueryData on the client side.

The file entry-hattip.tsx is Rakkas's server entry point and it contains Rakkas's server-side customization hooks. We use these hooks to serialize Tanstack Query's data into React's SSR stream.

The wrapApp hook is used to wrap the React app into a QueryClientProvider. A new query client is created for each request and its onSuccess hook is used to collect the data from each successful query.

The emitToDocumentHead hook is used to insert a small script into the document's head that contains the following:

$TQD=Object.create(null);$TQS=(data)=>Object.assign($TQD,data);

This defines a global object called $TQD (as in "Tanstack Query data") and a function called $TQS (as in "Tanstack Query set"). When $TQS is called with some data, it's merged into $TQD.

The emitBeforeSsrChunk is for injecting markup into React's SSR stream before each time React emits a chunk of HTML. We use it to inject a script that calls the $TQS function with the new data since the last time. The data is serialized with devalue, but JSON.stringify + some escaping would also work depending on your needs.

In entry-client.tsx, which is the client entry point of Rakkas, we take the data accumulated in $TQD and use queryClient.setQueryData to make it available to the client-side queries. We also overwrite the $TQS function to call queryClient.setQueryData from then on instead of stuffing the data into $TQD. We also delete the $TQD object to free up some memory.

We pass Date.now() to queryClient.setQueryData's updatedAt option. This is not really accurate but synchronizing clocks between the server and the client is not realistically feasible.

Finally, we use suspense: true when creating the query client to make sure all queries use Suspense by default. We also set less aggressive defaults to make debugging and observing the behavior easier.

You can uncomment the last few lines of layout.tsx to disable streaming and force React to wait until all suspense boundaries are resolved before emitting the HTML. This can make it easier to see what's going on.

TODO

  • Use Tanstack Query with useServerSideQuery
  • Create an "integration plugin" system for Rakkas to make this type of integration easier on the en user

About

Rakkas - @tanstack/react-query integration with streaming SSR with Suspense support

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages