<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged nuxt</title>
<link>https://coderlegion.com/tag/nuxt</link>
<description>Powered by Question2Answer</description>
<item>
<title>I Kept Rebuilding PDF Features in Vue 3, So I Built vue-pdf-export</title>
<link>https://coderlegion.com/26179/i-kept-rebuilding-pdf-features-in-vue-3-so-i-built-vue-pdf-export</link>
<description>&lt;p&gt;Hey everyone 👋&lt;/p&gt;
&lt;p&gt;I recently released &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/vue-pdf-export&quot;&gt;&lt;code&gt;vue-pdf-export&lt;/code&gt;&lt;/a&gt;, an open-source Vue 3 + TypeScript library for generating PDFs directly in the browser.&lt;/p&gt;
&lt;p&gt;I built it after repeatedly needing the same PDF features across different Vue projects and wanting one reusable solution instead of rebuilding the same logic each time.&lt;/p&gt;
&lt;h3 id=&quot;what-it-supports&quot;&gt;What it supports&lt;/h3&gt;
&lt;p&gt;Some of the main features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Canvas and selectable-text PDF rendering&lt;/li&gt;
&lt;li&gt;Automatic and manual pagination&lt;/li&gt;
&lt;li&gt;Preview, download, and browser print&lt;/li&gt;
&lt;li&gt;Headers and footers&lt;/li&gt;
&lt;li&gt;Watermarks and page targeting&lt;/li&gt;
&lt;li&gt;Clickable PDF links&lt;/li&gt;
&lt;li&gt;Password protection and permissions&lt;/li&gt;
&lt;li&gt;PDF metadata and compression&lt;/li&gt;
&lt;li&gt;Progress reporting and cancellation&lt;/li&gt;
&lt;li&gt;Large-document rendering&lt;/li&gt;
&lt;li&gt;Blob, ArrayBuffer, Base64, and PDF data URL outputs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data-pdf-ignore&lt;/code&gt; for excluding UI elements from exports&lt;/li&gt;
&lt;li&gt;Export-only page and body styling&lt;/li&gt;
&lt;li&gt;Nuxt 3 and Nuxt 4 SSR-safe package imports&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The package also has compatibility coverage for npm, pnpm, Yarn, Yarn Plug'n'Play, and Bun.&lt;/p&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install vue-pdf-export
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;basic-usage&quot;&gt;Basic usage&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from 'vue'
import { Html2Pdf } from 'vue3-pdf-export'
import 'vue3-pdf-export/style.css'

const pdf = ref&amp;lt;InstanceType&amp;lt;typeof Html2Pdf&amp;gt; | null&amp;gt;(null)

async function generate() {
  await pdf.value?.generatePdf()
}
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;generate&quot;&amp;gt;
    Generate PDF
  &amp;lt;/button&amp;gt;

  &amp;lt;Html2Pdf
    ref=&quot;pdf&quot;
    filename=&quot;example.pdf&quot;
    :preview-modal=&quot;true&quot;
  &amp;gt;
    &amp;lt;template #pdf-content&amp;gt;
      &amp;lt;main&amp;gt;
        &amp;lt;h1&amp;gt;Hello from Vue&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;This content will be exported to PDF.&amp;lt;/p&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/template&amp;gt;
  &amp;lt;/Html2Pdf&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;canvas-or-selectable-text&quot;&gt;Canvas or selectable text&lt;/h3&gt;
&lt;p&gt;Use Canvas rendering when HTML/CSS fidelity is the priority:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;Html2Pdf render-mode=&quot;canvas&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or use selectable-text rendering when users need to select, search, and copy text from the generated PDF:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;Html2Pdf render-mode=&quot;text&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes the package useful for both highly visual layouts and text-heavy documents such as reports, invoices, statements, and documentation.&lt;/p&gt;
&lt;p&gt;The idea is to let developers choose between maximum browser-style fidelity and real selectable PDF text depending on their use case.&lt;/p&gt;
&lt;h3 id=&quot;programmatic-output&quot;&gt;Programmatic output&lt;/h3&gt;
&lt;p&gt;The generated PDF can also be used directly in your application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const blob = await pdf.value?.generatePdf()
const buffer = await pdf.value?.generatePdfArrayBuffer()
const base64 = await pdf.value?.generatePdfBase64()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful when you want to upload, store, preview, or pass the PDF to another API instead of immediately downloading it.&lt;/p&gt;
&lt;h3 id=&quot;nuxt-support&quot;&gt;Nuxt support&lt;/h3&gt;
&lt;p&gt;The package is designed so importing it does not crash Nuxt SSR.&lt;/p&gt;
&lt;p&gt;Actual PDF generation still happens in the browser, so a typical Nuxt integration can use &lt;code&gt;ClientOnly&lt;/code&gt; or lazy client-side rendering when needed.&lt;/p&gt;
&lt;h3 id=&quot;pagination&quot;&gt;Pagination&lt;/h3&gt;
&lt;p&gt;The package supports both automatic and manual pagination.&lt;/p&gt;
&lt;p&gt;For an explicit manual page break:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&quot;html2pdf__page-break&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also options for keeping elements together and avoiding unwanted page breaks.&lt;/p&gt;
&lt;h3 id=&quot;large-document-rendering&quot;&gt;Large-document rendering&lt;/h3&gt;
&lt;p&gt;Large PDFs were one of the areas I wanted to improve significantly.&lt;/p&gt;
&lt;p&gt;Canvas rendering can become expensive when a browser attempts to create one extremely tall canvas for an entire document.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vue3-pdf-export&lt;/code&gt; can now render paginated Canvas documents incrementally, page by page, instead of depending on one giant full-document capture.&lt;/p&gt;
&lt;p&gt;In one benchmark fixture, the previous long-document capture surface was around:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;99.96 million pixels
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;while incremental rendering kept the maximum capture surface around:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1.74 million pixels per page
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is approximately a 98.26% reduction in peak capture-surface geometry for that fixture.&lt;/p&gt;
&lt;p&gt;This is specifically a rendering-surface measurement, not a claim that total browser memory always drops by the same percentage.&lt;/p&gt;
&lt;p&gt;The current regression suite includes documents across:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;10 / 25 / 40 / 50 / 60 / 75 / 100 pages
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;50-page and 100-page Canvas documents are part of the current verified test range.&lt;/p&gt;
&lt;p&gt;100 pages is not a hard package limit. Practical limits still depend on the document, images, browser memory, rendering mode, security settings, and the device being used.&lt;/p&gt;
&lt;h3 id=&quot;cancellation&quot;&gt;Cancellation&lt;/h3&gt;
&lt;p&gt;Long-running PDF generation can now be cancelled.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;pdf.value?.cancelGeneration()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cancellation is cooperative and works across the generation pipeline.&lt;/p&gt;
&lt;p&gt;The library also tests recovery after cancellation, so a new PDF can be generated after an earlier operation was cancelled.&lt;/p&gt;
&lt;p&gt;Some already-running synchronous browser operations cannot always be stopped immediately, but the pipeline checks for cancellation between major stages.&lt;/p&gt;
&lt;h3 id=&quot;page-aware-progress&quot;&gt;Page-aware progress&lt;/h3&gt;
&lt;p&gt;Progress reporting now includes more than a simple percentage.&lt;/p&gt;
&lt;p&gt;The package can expose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;current progress&lt;/li&gt;
&lt;li&gt;current page&lt;/li&gt;
&lt;li&gt;total pages&lt;/li&gt;
&lt;li&gt;current processing stage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stages include:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;pagination
images
preparing
rendering
watermark
header
footer
metadata
serializing
complete
error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes it possible to build UI such as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Rendering page 18 of 50...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;instead of showing an indefinite spinner.&lt;/p&gt;
&lt;h3 id=&quot;headers&quot;&gt;Headers&lt;/h3&gt;
&lt;p&gt;Headers can contain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;plain text&lt;/li&gt;
&lt;li&gt;trusted HTML&lt;/li&gt;
&lt;li&gt;optional logos&lt;/li&gt;
&lt;li&gt;custom backgrounds and text colors&lt;/li&gt;
&lt;li&gt;configurable logo dimensions&lt;/li&gt;
&lt;li&gt;selected-page targeting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A header can appear on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all pages&lt;/li&gt;
&lt;li&gt;the first page only&lt;/li&gt;
&lt;li&gt;the last page only&lt;/li&gt;
&lt;li&gt;every page except the first&lt;/li&gt;
&lt;li&gt;explicit page numbers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Selective header targeting also uses page-aware spacing, so header space is reserved only where the header is actually rendered.&lt;/p&gt;
&lt;h3 id=&quot;footers&quot;&gt;Footers&lt;/h3&gt;
&lt;p&gt;Footers support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML mode&lt;/li&gt;
&lt;li&gt;text mode&lt;/li&gt;
&lt;li&gt;trusted HTML content&lt;/li&gt;
&lt;li&gt;optional logos&lt;/li&gt;
&lt;li&gt;configurable logo dimensions&lt;/li&gt;
&lt;li&gt;background and text colors&lt;/li&gt;
&lt;li&gt;page-number templates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Page {n} of {total}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Header and footer logos preserve their aspect ratio instead of being stretched into a fixed box.&lt;/p&gt;
&lt;h4 id=&quot;footer-performance-improvements&quot;&gt;Footer performance improvements&lt;/h4&gt;
&lt;p&gt;Static HTML footers are now rendered once and reused across pages instead of being rasterized repeatedly.&lt;/p&gt;
&lt;p&gt;In project benchmark fixtures:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;HTML footer without logo
7.82s -&amp;gt; 1.43s

HTML footer with logo
11.81s -&amp;gt; 1.29s

Footer-heavy PDF size
~46.7 MB -&amp;gt; ~1.0 MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These numbers are specific to the benchmark fixtures and should not be treated as universal performance guarantees.&lt;/p&gt;
&lt;h3 id=&quot;repeated-image-optimization&quot;&gt;Repeated image optimization&lt;/h3&gt;
&lt;p&gt;Selectable-text rendering now caches repeated images within a single generation.&lt;/p&gt;
&lt;p&gt;If the same image is reused across many pages, the package can avoid repeatedly loading and encoding the same source.&lt;/p&gt;
&lt;p&gt;The cache is scoped to one PDF generation and is cleared afterward.&lt;/p&gt;
&lt;p&gt;This is especially useful for documents containing repeated logos, icons, product images, or other shared assets.&lt;/p&gt;
&lt;h3 id=&quot;resource-cleanup&quot;&gt;Resource cleanup&lt;/h3&gt;
&lt;p&gt;A lot of work also went into cleaning temporary resources earlier.&lt;/p&gt;
&lt;p&gt;The generation pipeline now handles cleanup of things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;temporary cloned DOM nodes&lt;/li&gt;
&lt;li&gt;temporary canvases&lt;/li&gt;
&lt;li&gt;encoded image data&lt;/li&gt;
&lt;li&gt;per-generation image caches&lt;/li&gt;
&lt;li&gt;rendering state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Canvas backing stores are also released when possible after they are no longer needed.&lt;/p&gt;
&lt;p&gt;Repeated-generation and isolation tests were added to help detect regressions in cleanup behavior.&lt;/p&gt;
&lt;h3 id=&quot;browser-responsiveness&quot;&gt;Browser responsiveness&lt;/h3&gt;
&lt;p&gt;Large PDF generation is naturally expensive, especially in Canvas mode.&lt;/p&gt;
&lt;p&gt;The pipeline now yields between selected expensive operations so the browser gets opportunities to process UI work while generation is running.&lt;/p&gt;
&lt;p&gt;Combined with cancellation and page-aware progress, this makes long-running generation easier to integrate into real applications.&lt;/p&gt;
&lt;h3 id=&quot;watermarks&quot;&gt;Watermarks&lt;/h3&gt;
&lt;p&gt;Watermarks can be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;text or image&lt;/li&gt;
&lt;li&gt;foreground or background&lt;/li&gt;
&lt;li&gt;single or repeated&lt;/li&gt;
&lt;li&gt;rotated&lt;/li&gt;
&lt;li&gt;positioned&lt;/li&gt;
&lt;li&gt;configured with opacity&lt;/li&gt;
&lt;li&gt;targeted to selected PDF pages&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pdf-security&quot;&gt;PDF security&lt;/h3&gt;
&lt;p&gt;You can optionally generate password-protected PDFs with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user/open password&lt;/li&gt;
&lt;li&gt;owner password&lt;/li&gt;
&lt;li&gt;configurable permissions&lt;/li&gt;
&lt;li&gt;password construction from multiple password parts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Supported permission controls include:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;print
modify
copy
annot-forms
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;metadata-and-compression&quot;&gt;Metadata and compression&lt;/h3&gt;
&lt;p&gt;PDF metadata can include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;author&lt;/li&gt;
&lt;li&gt;subject&lt;/li&gt;
&lt;li&gt;keywords&lt;/li&gt;
&lt;li&gt;creator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;jsPDF stream compression can also be enabled when needed.&lt;/p&gt;
&lt;h3 id=&quot;clickable-links&quot;&gt;Clickable links&lt;/h3&gt;
&lt;p&gt;PDF link annotations are also supported, allowing links in generated documents to remain interactive instead of becoming plain visual text.&lt;/p&gt;
&lt;h3 id=&quot;safer-filenames&quot;&gt;Safer filenames&lt;/h3&gt;
&lt;p&gt;Generated filenames are normalized before download.&lt;/p&gt;
&lt;p&gt;This includes handling invalid filename characters, whitespace, reserved Windows names, trailing dots or spaces, and overly long filenames.&lt;/p&gt;
&lt;p&gt;Unicode filenames are still preserved.&lt;/p&gt;
&lt;h3 id=&quot;a-note-about-encrypted-raster-pdfs&quot;&gt;A note about encrypted raster PDFs&lt;/h3&gt;
&lt;p&gt;While profiling larger secured documents, I found that encrypted raster PDFs could retain significantly more browser memory than equivalent unencrypted PDFs.&lt;/p&gt;
&lt;p&gt;I reproduced the behavior directly with jsPDF outside the main &lt;code&gt;vue3-pdf-export&lt;/code&gt; rendering pipeline.&lt;/p&gt;
&lt;p&gt;That means a substantial part of this behavior appears to come from the underlying PDF library rather than from the Vue component itself.&lt;/p&gt;
&lt;p&gt;I opened an upstream jsPDF issue with the reproduction and benchmark details:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/parallax/jsPDF/issues/4017&quot;&gt;https://github.com/parallax/jsPDF/issues/4017&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For large secured documents, selectable-text mode can sometimes be a useful alternative because text-based PDFs are generally much smaller than equivalent rasterized documents.&lt;/p&gt;
&lt;p&gt;This is still workload-dependent, so applications generating large encrypted PDFs should test their own representative content.&lt;/p&gt;
&lt;h3 id=&quot;testing-and-performance-work&quot;&gt;Testing and performance work&lt;/h3&gt;
&lt;p&gt;The project now includes automated testing around:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unit behavior&lt;/li&gt;
&lt;li&gt;Chromium&lt;/li&gt;
&lt;li&gt;Firefox&lt;/li&gt;
&lt;li&gt;WebKit&lt;/li&gt;
&lt;li&gt;large documents&lt;/li&gt;
&lt;li&gt;Canvas rendering&lt;/li&gt;
&lt;li&gt;selectable-text rendering&lt;/li&gt;
&lt;li&gt;cancellation and recovery&lt;/li&gt;
&lt;li&gt;image-heavy documents&lt;/li&gt;
&lt;li&gt;repeated images&lt;/li&gt;
&lt;li&gt;footer optimization&lt;/li&gt;
&lt;li&gt;browser cleanup&lt;/li&gt;
&lt;li&gt;security behavior&lt;/li&gt;
&lt;li&gt;memory measurements&lt;/li&gt;
&lt;li&gt;repeated generation&lt;/li&gt;
&lt;li&gt;stress testing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The current unit suite contains:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;19 test files
106 tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also dedicated benchmark and Playwright workflows for performance-heavy scenarios.&lt;/p&gt;
&lt;p&gt;For heavy PDF tests, I intentionally run browser work sequentially where appropriate so multiple workers do not distort memory measurements.&lt;/p&gt;
&lt;h3 id=&quot;why-i-built-a-vue-layer-instead-of-replacing-the-underlying-libraries&quot;&gt;Why I built a Vue layer instead of replacing the underlying libraries&lt;/h3&gt;
&lt;p&gt;The goal was never to pretend that &lt;code&gt;html2pdf.js&lt;/code&gt;, &lt;code&gt;html2canvas&lt;/code&gt;, or &lt;code&gt;jsPDF&lt;/code&gt; do not exist.&lt;/p&gt;
&lt;p&gt;Those libraries still provide important pieces of the Canvas rendering pipeline.&lt;/p&gt;
&lt;p&gt;The goal of &lt;code&gt;vue3-pdf-export&lt;/code&gt; is to provide a Vue-oriented layer around PDF generation so common production requirements do not need to be rebuilt for every application.&lt;/p&gt;
&lt;p&gt;At the same time, developers can still pass advanced html2pdf.js and jsPDF options when the higher-level API is not enough.&lt;/p&gt;
&lt;p&gt;The selectable-text renderer provides another option for cases where real PDF text matters more than exact Canvas-style browser rendering.&lt;/p&gt;
&lt;h3 id=&quot;current-release&quot;&gt;Current release&lt;/h3&gt;
&lt;p&gt;The current release is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;vue-pdf-export v1.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The project has grown significantly since the original release, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Canvas and selectable-text rendering modes&lt;/li&gt;
&lt;li&gt;incremental page-by-page Canvas generation&lt;/li&gt;
&lt;li&gt;large-document regression coverage&lt;/li&gt;
&lt;li&gt;cancellation and recovery&lt;/li&gt;
&lt;li&gt;current-page / total-page progress&lt;/li&gt;
&lt;li&gt;repeated-image caching&lt;/li&gt;
&lt;li&gt;footer rendering optimization&lt;/li&gt;
&lt;li&gt;earlier DOM and Canvas cleanup&lt;/li&gt;
&lt;li&gt;browser responsiveness improvements&lt;/li&gt;
&lt;li&gt;PDF security&lt;/li&gt;
&lt;li&gt;metadata and compression&lt;/li&gt;
&lt;li&gt;clickable links&lt;/li&gt;
&lt;li&gt;trusted HTML headers and footers&lt;/li&gt;
&lt;li&gt;logo support&lt;/li&gt;
&lt;li&gt;watermarks&lt;/li&gt;
&lt;li&gt;filename normalization&lt;/li&gt;
&lt;li&gt;expanded unit, browser, stress, and performance testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;try-it&quot;&gt;Try it&lt;/h3&gt;
&lt;p&gt;🧪 &lt;strong&gt;Live Playground&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://vue-pdf-export-playground.vercel.app/&quot;&gt;https://vue-pdf-export-playground.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📖 &lt;strong&gt;Documentation&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://vue-pdf-export-docs.vercel.app/&quot;&gt;https://vue-pdf-export-docs.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📦 &lt;strong&gt;npm&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/vue-pdf-export&quot;&gt;https://www.npmjs.com/package/vue-pdf-export&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;💬 &lt;strong&gt;Feature requests &amp;amp; feedback&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://vue3-pdf-export.canny.io/&quot;&gt;https://vue3-pdf-export.canny.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I’d really appreciate feedback from Vue and Nuxt developers, especially around real-world PDF edge cases, API ergonomics, pagination, large documents, or features you would expect from a Vue PDF library.&lt;/p&gt;
&lt;p&gt;If you are building a Vue application that needs client-side PDF generation, I would love to hear about the kinds of documents you are generating and the edge cases you run into.&lt;/p&gt;
&lt;p&gt;The project is still evolving, and real-world feedback is one of the most useful inputs for deciding what to improve next. If you find a bug, performance cases, or have an idea for an API improvement, feel free to let me know.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26179/i-kept-rebuilding-pdf-features-in-vue-3-so-i-built-vue-pdf-export</guid>
<pubDate>Fri, 04 Sep 2026 00:10:12 +0000</pubDate>
</item>
<item>
<title>Testing Nuxt apps in the real browser, against a real backend</title>
<link>https://coderlegion.com/20850/testing-nuxt-apps-in-the-real-browser-against-a-real-backend</link>
<description>&lt;p&gt;You just built a Nuxt todo page. It loads from a Nitro route, adds a todo, deletes one, and it works when you click around. Now you want a test that proves it keeps working.&lt;/p&gt;
&lt;p&gt;The usual answer is to leave the app, stand up a separate test stack, and rebuild the page in a fake environment with every route mocked. TWD takes the other path: test it where you already are, while you are still building it.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/twd-js&quot;&gt;TWD&lt;/a&gt; runs your tests inside the Nuxt dev server, in the real browser tab where the app already runs. The same todo page gets tested end to end, against your real backend. No route mocks, no separate e2e stack, no jsdom.&lt;/p&gt;
&lt;p&gt;Here is the full setup.&lt;/p&gt;
&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install --save-dev twd-js
npx twd-js init public --save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the Vite plugin in &lt;code&gt;nuxt.config.ts&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { twd } from 'twd-js/vite-plugin'

export default defineNuxtConfig({
  vite: {
    plugins: [
      twd({ testFilePattern: '/**/*.twd.test.{ts,tsx}' }),
    ],
  },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two Nuxt-specific bits to wire up, both one-time.&lt;/p&gt;
&lt;h3 id=&quot;1-inject-the-sidebar-in-app-vue&quot;&gt;1. Inject the sidebar in app.vue&lt;/h3&gt;
&lt;p&gt;Nuxt does not serve a Vite &lt;code&gt;index.html&lt;/code&gt;. Nitro renders the HTML, so the plugin cannot auto-inject its sidebar script. Point your app at the virtual module yourself, dev-only, in &lt;code&gt;app.vue&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
if (import.meta.dev) {
  useHead({
    script: [{ src: '/_nuxt/@id/virtual:twd/init', type: 'module' }],
  })
}
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;NuxtPage /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note the &lt;code&gt;/_nuxt/&lt;/code&gt; prefix, that is Nuxt's Vite base. The &lt;code&gt;import.meta.dev&lt;/code&gt; guard keeps the tag out of production.&lt;/p&gt;
&lt;h3 id=&quot;2-reload-the-browser-when-a-test-file-changes&quot;&gt;2. Reload the browser when a test file changes&lt;/h3&gt;
&lt;p&gt;On Nuxt 4 with Vite 7, editing a &lt;code&gt;.twd.test&lt;/code&gt; file does not reload the browser on its own. This small plugin fixes it: it watches your test files and triggers a reload when one changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// npm install --save-dev chokidar
import chokidar from 'chokidar'
import type { Plugin } from 'vite'

function twdTestHmr(): Plugin {
  const isTestFile = (file: string) =&amp;gt; /\.twd\.test\.tsx?$/.test(file)
  return {
    name: 'twd-test-hmr',
    apply: 'serve',
    configureServer(server) {
      // Under Nuxt, Vite's config.root is the app/ srcDir, where the tests live.
      const watcher = chokidar.watch(server.config.root, {
        ignored: (path) =&amp;gt; /node_modules|\.nuxt|\.output|\.git/.test(path),
        ignoreInitial: true,
      })
      const reload = (file: string) =&amp;gt; {
        if (!isTestFile(file)) return
        // Drop the cached module so the reload serves fresh code.
        const graph = server.environments?.client?.moduleGraph ?? server.moduleGraph
        for (const mod of graph.getModulesByFile(file) ?? []) {
          graph.invalidateModule(mod)
        }
        server.ws.send({ type: 'full-reload', path: '*' })
      }
      watcher.on('change', reload).on('add', reload).on('unlink', reload)
      server.httpServer?.once('close', () =&amp;gt; void watcher.close())
    },
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add &lt;code&gt;twdTestHmr()&lt;/code&gt; to the &lt;code&gt;plugins&lt;/code&gt; array next to &lt;code&gt;twd()&lt;/code&gt;. Edit a test, the browser reloads.&lt;/p&gt;
&lt;p&gt;Run &lt;code&gt;npm run dev&lt;/code&gt;. Sidebar on the left, your app on the right.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dyn3qub25h2566oo5wv.png&quot; alt=&quot;Nuxt app with twd tests running&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;testing-against-the-real-backend&quot;&gt;Testing against the real backend&lt;/h3&gt;
&lt;p&gt;You do not mock routes. The tests hit your dev server and your database for real. The only thing you need is a clean starting point for each test.&lt;/p&gt;
&lt;p&gt;A dev-only endpoint gives you that. It resets the database to a seeded baseline, guarded so it never exists in production:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// server/api/__test/reset.post.ts
export default defineEventHandler(() =&amp;gt; {
  if (!import.meta.dev) {
    throw createError({ statusCode: 404, statusMessage: 'Not found' })
  }
  resetDb()
  return { ok: true, todos: listTodos() }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;resetDb()&lt;/code&gt; deletes every row, resets the id sequence so seeded ids are deterministic, and re-seeds. The &lt;code&gt;import.meta.dev&lt;/code&gt; guard means the route returns 404 in a production build.&lt;/p&gt;
&lt;p&gt;Call it before each test, then drive the real UI:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { twd, userEvent, screenDom } from 'twd-js'
import { describe, it, beforeEach } from 'twd-js/runner'

describe('Todos (real SQLite backend)', () =&amp;gt; {
  beforeEach(async () =&amp;gt; {
    // Reset the database to its seeded state. No route mocking.
    await fetch('/api/__test/reset', { method: 'POST' })
  })

  it('creates a todo end to end', async () =&amp;gt; {
    await twd.visit('/todos')
    await userEvent.type(screenDom.getByTestId('todo-input'), 'Buy milk')
    await userEvent.click(screenDom.getByTestId('todo-add'))

    twd.should(await screenDom.findByText('Buy milk'), 'be.visible')
  })
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No mock rules. The &lt;code&gt;POST&lt;/code&gt; persists to the database, the list refreshes, and the assertion runs against the real rendered DOM. Delete a row and assert it is gone, same shape. When a test fails, it failed against the real backend, not against a mock you forgot to update.&lt;/p&gt;
&lt;h3 id=&quot;try-it-1&quot;&gt;Try it&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Sample repo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/BRIKEV/twd-nuxt-example&quot;&gt;BRIKEV/twd-nuxt-example&lt;/a&gt;. Nuxt 4, a SQLite-backed todo list, the HMR plugin, the dev-only reset endpoint, and tests that run against the real backend.&lt;/li&gt;
&lt;li&gt;Docs: &lt;a rel=&quot;nofollow&quot; href=&quot;https://twd.dev&quot;&gt;twd.dev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install --save-dev twd-js
npx twd-js init public --save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the plugin to &lt;code&gt;nuxt.config.ts&lt;/code&gt;, drop the dev-only script into &lt;code&gt;app.vue&lt;/code&gt;, add the HMR plugin, write your first &lt;code&gt;.twd.test.ts&lt;/code&gt;. The sidebar opens itself.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20850/testing-nuxt-apps-in-the-real-browser-against-a-real-backend</guid>
<pubDate>Wed, 17 Jun 2026 22:17:03 +0000</pubDate>
</item>
<item>
<title>✨ A New Stack — Introducing Redis Nuxt Blog: A Sci-Fi Blog Boilerplate from a Galaxy Not So Far Away</title>
<link>https://coderlegion.com/15670/new-stack-introducing-redis-nuxt-blog-sci-fi-blog-boilerplate-from-a-galaxy-not-so-far-away</link>
<description>&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;“A long time ago, in a server rack far, far away, a lone developer sought a faster way to publish their thoughts across the galaxy…”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you’re a developer roaming the cosmic void in search of the perfect blogging platform — one fast enough to outrun hyperspace lag, simple enough to configure from a single holocron file, and powered by the mighty Force of Redis — your quest ends here.&lt;/p&gt;
&lt;p&gt;Welcome to &lt;strong&gt;Redis Nuxt Blog&lt;/strong&gt;, a futuristic, high-performance boilerplate forged with &lt;strong&gt;Nuxt 3&lt;/strong&gt;, &lt;strong&gt;Redis Stack&lt;/strong&gt;, and a containerized fleet of &lt;strong&gt;Docker Star Destroyers&lt;/strong&gt;.&lt;br&gt;
Inspired by the legendary &lt;em&gt;Salvatore “antirez” Sanfilippo&lt;/em&gt;, creator of Redis and intergalactic craftsman of absurdly fast data structures, this project aims to honor that sci-fi spirit — equal parts elegance, speed, and space-wizardry.&lt;/p&gt;
&lt;p&gt;Grab your lightsabers, devs. We’re jumping to lightspeed. ⚡&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/melasistema/redis-blog/master/resources/images/redis-blog-poster-image.jpg&quot; alt=&quot;Redis Nuxt Blog&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-another-blog-boilerplate-&quot;&gt;Why Another Blog Boilerplate?&lt;/h3&gt;
&lt;p&gt;Because most blog starters are like broken droids — sluggish, uncustomizable, and likely to beep something rude at you when you change a font.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Redis Nuxt Blog&lt;/strong&gt; is built for developers who want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A fast, SSR-friendly, SEO-happy front-end&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A database engine so quick it would embarrass a pod-racer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A deeply configurable UI without fiddling with 27 separate config files&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A ready-to-launch Docker setup&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A CLI so efficient it feels like talking to R2-D2 (but without the panic screams)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A theming system powered by Tailwind CSS, customizable from one file&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Markdown-powered post content&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dynamic excerpts and content width control&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-core-features-translated-from-wookiee-&quot;&gt;✨ Core Features (Translated from Wookiee)&lt;/h3&gt;
&lt;h4 id=&quot;nuxt-js-3-frontend&quot;&gt;Nuxt.js 3 Frontend&lt;/h4&gt;
&lt;p&gt;Modern. Fast. SSR-powered.&lt;br&gt;
Perfect for blogs, portals, and broadcasting your galactic manifestos.&lt;/p&gt;
&lt;h4 id=&quot;-redis-stack-as-the-primary-database&quot;&gt;⚡ Redis Stack as the Primary Database&lt;/h4&gt;
&lt;p&gt;Forget SQL. Forget slow disks.&lt;br&gt;
We harness the &lt;strong&gt;Quantum Hyperdrive&lt;/strong&gt; (a.k.a RedisJSON + Redis Sorted Sets) to store posts with blazing speed.&lt;/p&gt;
&lt;p&gt;Salvatore Sanfilippo didn’t intend Redis to be a sci-fi artifact… but let’s be honest, it might as well have been forged from midichlorians.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/melasistema/redis-blog/master/resources/images/redis-blog-hyperdrive-performance.jpg&quot; alt=&quot;Redis Nuxt Blog&quot;&gt;&lt;/p&gt;
&lt;h4 id=&quot;one-config-file-to-rule-them-all-now-with-full-tailwind-integration-&quot;&gt;One Config File to Rule Them All (Now With Full Tailwind Integration)&lt;/h4&gt;
&lt;p&gt;Every visual setting — fonts, weights, palette, spacing, even content width — comes from:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;config/blog.config.ts&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;This single file now controls:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Typography per heading level&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Full Tailwind color palette generation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Global content max-width&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Header title &amp;amp; tagline&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Layout constraints&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Balance to the styling Force, at last.&lt;/p&gt;
&lt;h4 id=&quot;-post-excerpts-new-&quot;&gt;✂️ Post Excerpts (New!)&lt;/h4&gt;
&lt;p&gt;Enable &lt;strong&gt;post previews&lt;/strong&gt; on the homepage with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;postExcerpt.enabled&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;postExcerpt.maxLength&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Perfect for teasing your readers like a Holonet trailer.&lt;/p&gt;
&lt;h4 id=&quot;-post-navigation&quot;&gt;↔️ Post Navigation&lt;/h4&gt;
&lt;p&gt;Navigate between posts like browsing encrypted Jedi archives.&lt;/p&gt;
&lt;h4 id=&quot;markdown-support&quot;&gt;Markdown Support&lt;/h4&gt;
&lt;p&gt;Posts are fully Markdown-powered — headings, lists, code blocks, even multi-line input via CLI.&lt;/p&gt;
&lt;p&gt;Write your galactic knowledge in pure Markdown. No HTML acrobatics needed.&lt;/p&gt;
&lt;h4 id=&quot;dockerized-everything&quot;&gt;Dockerized Everything&lt;/h4&gt;
&lt;p&gt;Spin up Nuxt + Redis with one command:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;docker compose up -d --build&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;Your dev environment becomes as consistent as Stormtrooper aim (but… good).&lt;/p&gt;
&lt;h4 id=&quot;content-cli-upgraded-&quot;&gt;Content CLI (Upgraded!)&lt;/h4&gt;
&lt;p&gt;Create, list, delete posts with simple commands:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm run new-post
npm run post:list
npm run post:delete&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;The CLI now supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Multi-line Markdown input&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Automatically generated slugs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Default author fallback&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You are now the administrator of your own Holocron Library.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-tech-stack-what-our-starship-is-made-of&quot;&gt;️ Tech Stack: What Our Starship Is Made Of&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Nuxt 3&lt;/strong&gt; — the elegant lightsaber&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Redis Stack w/ RedisJSON&lt;/strong&gt; — the hyperdrive core&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tailwind CSS&lt;/strong&gt; — the robe of a modern Jedi developer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google Fonts module&lt;/strong&gt; — for typographic wizardry&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker Compose&lt;/strong&gt; — the Imperial Fleet&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TypeScript + tsx runtime&lt;/strong&gt; — because even smugglers need types&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/melasistema/redis-blog/master/resources/images/redis-blog-the-ultimate-tech-stack.jpg&quot; alt=&quot;Redis Nuxt Blog&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-the-holocron-of-configuration-updated-&quot;&gt;️ The Holocron of Configuration (Updated!)&lt;/h3&gt;
&lt;p&gt;Everything — fonts, colors, layout, excerpts, pagination — lives in:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;config/blog.config.ts&lt;/code&gt; &lt;/p&gt;
&lt;p&gt;New additions include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;headerTitle&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;headerTagline&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Dynamic Tailwind theme generation&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;contentMaxWidth&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Post excerpts config&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Want your blog to have an imperial blue palette?&lt;br&gt;
Done.&lt;/p&gt;
&lt;p&gt;Want H1 in &lt;em&gt;Englebert&lt;/em&gt; and H2 in &lt;em&gt;Roboto Slab&lt;/em&gt;?&lt;br&gt;
Absolutely.&lt;/p&gt;
&lt;p&gt;Want your content width to feel like the inside of a Star Destroyer corridor?&lt;br&gt;
Just set a new max-width.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;api-endpoints-for-the-droids-among-us-&quot;&gt;API Endpoints (For the Droids Among Us)&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Endpoint &lt;/th&gt;&lt;th&gt;Method &lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;/api/posts&lt;/code&gt; &lt;/td&gt;&lt;td&gt;GET &lt;/td&gt;&lt;td&gt;Retrieve posts. Supports &lt;code&gt;?page=N&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;/api/posts/:slug&lt;/code&gt; &lt;/td&gt;&lt;td&gt;GET &lt;/td&gt;&lt;td&gt;Fetch single post + neighbors&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;/api/tags&lt;/code&gt; &lt;/td&gt;&lt;td&gt;GET &lt;/td&gt;&lt;td&gt;Retrieve all tags&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Perfect for custom frontends, mobile apps, or your own droid companion.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-developers-love-this&quot;&gt;Why Developers Love This&lt;/h3&gt;
&lt;p&gt;Because &lt;strong&gt;simplicity&lt;/strong&gt; + &lt;strong&gt;power&lt;/strong&gt; = developer bliss.&lt;/p&gt;
&lt;p&gt;Because Redis is a piece of sci-fi technology disguised as a database.&lt;/p&gt;
&lt;p&gt;Because Nuxt 3 is smooth as sliding through hyperspace.&lt;/p&gt;
&lt;p&gt;Because Tailwind lets you customize everything without rewriting CSS.&lt;/p&gt;
&lt;p&gt;Because Markdown posts feel like writing Jedi scrolls.&lt;/p&gt;
&lt;p&gt;And because every post stored in Redis Sorted Sets is basically organized by the Force.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;author-amp-origin-story&quot;&gt;Author &amp;amp; Origin Story&lt;/h3&gt;
&lt;p&gt;Built by &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/melasistema&quot;&gt;Luca Visciola&lt;/a&gt;&lt;/strong&gt; — drummer, developer, and happy dad.&lt;/p&gt;
&lt;p&gt;Inspired by &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://antirez.com&quot;&gt;Salvatore “antirez” Sanfilippo&lt;/a&gt;&lt;/strong&gt;, whose creation Redis is widely believed to be running not on silicon… but on compressed fragments of a neutron star.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-transmission&quot;&gt;Final Transmission&lt;/h3&gt;
&lt;p&gt;If you're searching for a fast, flexible, sci-fi-friendly blog engine that handles posts faster than the Millennium Falcon outruns TIE Fighters, this boilerplate may be the one you’re looking for.&lt;/p&gt;
&lt;p&gt;Clone it.&lt;br&gt;
Customize it.&lt;br&gt;
Seed it.&lt;br&gt;
Deploy it.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;GitHub Repository:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/melasistema/redis-blog&quot;&gt;https://github.com/melasistema/redis-blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And may the &lt;strong&gt;Force of Redis&lt;/strong&gt; be with you. ❤️‍&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/15670/new-stack-introducing-redis-nuxt-blog-sci-fi-blog-boilerplate-from-a-galaxy-not-so-far-away</guid>
<pubDate>Thu, 23 Apr 2026 16:49:31 +0000</pubDate>
</item>
</channel>
</rss>