<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jak s</title>
    <description>The latest articles on DEV Community by Jak s (@jak_s_765bff302f0b7674066).</description>
    <link>https://dev.to/jak_s_765bff302f0b7674066</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3968601%2F325f46d9-03fa-4c69-8436-4935f18d5c92.png</url>
      <title>DEV Community: Jak s</title>
      <link>https://dev.to/jak_s_765bff302f0b7674066</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jak_s_765bff302f0b7674066"/>
    <language>en</language>
    <item>
      <title>5 Angular 17 performance wins that actually matter</title>
      <dc:creator>Jak s</dc:creator>
      <pubDate>Mon, 15 Jun 2026 14:22:44 +0000</pubDate>
      <link>https://dev.to/jak_s_765bff302f0b7674066/5-angular-17-performance-wins-that-actually-matter-3gjn</link>
      <guid>https://dev.to/jak_s_765bff302f0b7674066/5-angular-17-performance-wins-that-actually-matter-3gjn</guid>
      <description>&lt;p&gt;I've spent the last few years building large Angular applications for enterprise clients — data-heavy dashboards, real-time monitoring tools, document processors. Every project starts the same way: default change detection, BehaviorSubject everywhere, *ngFor without trackBy. By tip 3 or 4 in this list, the difference in rendering performance is visible without a profiler.&lt;/p&gt;

&lt;p&gt;Here are the five things I do on every project.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OnPush everywhere
1
Angular's default change detection checks every component on every event. ChangeDetectionStrategy.OnPush cuts this to only check a component when its @Input() references change, an async pipe emits, or you call markForCheck() explicitly. On a tree with 200+ components, this is the single biggest win.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;import { ChangeDetectionStrategy, Component } from '@angular/core';&lt;/p&gt;

&lt;p&gt;@Component({&lt;br&gt;
  selector: 'app-invoice-row',&lt;br&gt;
  templateUrl: './invoice-row.component.html',&lt;br&gt;
  changeDetection: ChangeDetectionStrategy.OnPush,&lt;br&gt;
})&lt;br&gt;
export class InvoiceRowComponent {&lt;br&gt;
  @Input() invoice!: Invoice;&lt;br&gt;
}&lt;br&gt;
The cost: you have to be more deliberate about when data changes. Mutating an object in place won't trigger change detection — you need to produce a new reference. That discipline is worth it for the performance gain, and it also makes your code easier to reason about.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Signals instead of BehaviorSubject
2
Angular 17's signals (signal(), computed(), effect()) are a leaner alternative to BehaviorSubject for local and shared state. They integrate directly with Angular's change detection — when a signal value changes, only the components that read it are marked dirty, with no manual subscription management.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;import { signal, computed } from '@angular/core';&lt;/p&gt;

&lt;p&gt;// In a service or component:&lt;br&gt;
const invoices = signal([]);&lt;br&gt;
const totalAmount = computed(() =&amp;gt;&lt;br&gt;
  invoices().reduce((sum, inv) =&amp;gt; sum + inv.total, 0)&lt;br&gt;
);&lt;/p&gt;

&lt;p&gt;// To update:&lt;br&gt;
invoices.update(prev =&amp;gt; [...prev, newInvoice]);&lt;/p&gt;

&lt;p&gt;// In the template — no async pipe needed:&lt;br&gt;
// {{ totalAmount() }}&lt;br&gt;
computed() memoises automatically — it only recalculates when one of its signal dependencies changes. Replace your derived BehaviorSubject chains with computed() and you get lazy evaluation for free.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Virtual scrolling with CDK
3
Rendering a list of 5,000 rows creates 5,000 DOM nodes. Virtual scrolling renders only the nodes visible in the viewport, typically 20–50 items, and recycles them as the user scrolls. The Angular CDK provides this out of the box.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// Install: npm install @angular/cdk&lt;br&gt;
// app.module.ts or standalone imports:&lt;br&gt;
import { ScrollingModule } from '@angular/cdk/scrolling';&lt;/p&gt;

&lt;p&gt;// Template:&lt;br&gt;
&lt;br&gt;
  &lt;/p&gt;
       class="invoice-row"&amp;gt;&lt;br&gt;
    {{ invoice.vendor_name }} — {{ invoice.total | currency }}&lt;br&gt;
  &lt;br&gt;
&lt;br&gt;
itemSize is the fixed height of each row in pixels. For variable-height items, use AutoSizeVirtualScrollStrategy from the experimental package — it measures items and adjusts, though it's slightly more expensive to initialise.

&lt;ol&gt;
&lt;li&gt;trackBy / track in &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; — prevent DOM thrashing
4
Without identity tracking, Angular destroys and recreates every DOM node when a list is re-assigned, even if only one item changed. trackBy tells Angular how to identify items across re-renders, so only genuinely changed or new items touch the DOM.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the legacy *ngFor syntax:&lt;/p&gt;


  {{ invoice.vendor_name }}


&lt;p&gt;trackById(index: number, invoice: Invoice): string {&lt;br&gt;
  return invoice.id;&lt;br&gt;
}&lt;br&gt;
In Angular 17's new &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; control flow (preferred for new code), track is mandatory — the compiler enforces it:&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; (invoice of invoices; track invoice.id) {&lt;br&gt;
  &lt;br&gt;
}&lt;br&gt;
Use a stable, unique property like a database ID or UUID. Using index as the track key is better than nothing but still causes unnecessary re-renders when items are reordered or inserted in the middle.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lazy-loaded standalone components
5
Module-based lazy loading is well known, but with standalone components in Angular 17 you can lazy-load individual components directly from a route — no NgModule wrapper required. The component and its entire dependency tree are split into a separate chunk and fetched only when the route is first navigated to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// app.routes.ts&lt;br&gt;
import { Routes } from '@angular/router';&lt;/p&gt;

&lt;p&gt;export const routes: Routes = [&lt;br&gt;
  {&lt;br&gt;
    path: 'dashboard',&lt;br&gt;
    loadComponent: () =&amp;gt;&lt;br&gt;
      import('./dashboard/dashboard.component')&lt;br&gt;
        .then(m =&amp;gt; m.DashboardComponent),&lt;br&gt;
  },&lt;br&gt;
  {&lt;br&gt;
    path: 'invoices',&lt;br&gt;
    loadComponent: () =&amp;gt;&lt;br&gt;
      import('./invoices/invoice-list.component')&lt;br&gt;
        .then(m =&amp;gt; m.InvoiceListComponent),&lt;br&gt;
  },&lt;br&gt;
];&lt;br&gt;
Combine this with PreloadAllModules or a custom preloading strategy to pre-fetch chunks after the initial route has loaded, giving you the best of both worlds: fast initial load and near-instant subsequent navigation.&lt;/p&gt;

&lt;p&gt;// main.ts or app.config.ts&lt;br&gt;
import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router';&lt;/p&gt;

&lt;p&gt;provideRouter(routes, withPreloading(PreloadAllModules))&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Measure first — profile before you optimise
Applying optimisations blindly wastes time and can introduce bugs. Before touching change detection or lazy loading, measure where the actual bottleneck is. Two tools I always use:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Angular DevTools (Chrome extension) gives you a component tree with render times. Sort by time spent and you'll find the real offenders within minutes — usually one or two components responsible for 80% of the render cost.&lt;/p&gt;

&lt;p&gt;Bundle analyser: run ng build --stats-json then feed the output to webpack-bundle-analyzer. You'll immediately see which third-party libraries are bloating your initial bundle. I've seen projects shave 200KB off their initial load just by switching from moment.js to date-fns or by moving a charting library behind a lazy route.&lt;/p&gt;
&lt;h1&gt;
  
  
  Build with stats
&lt;/h1&gt;

&lt;p&gt;ng build --stats-json&lt;/p&gt;
&lt;h1&gt;
  
  
  Analyse the bundle
&lt;/h1&gt;

&lt;p&gt;npx webpack-bundle-analyzer dist/your-app/stats.json&lt;br&gt;
Target a First Contentful Paint under 1.8s and a Total Blocking Time under 200ms on a mid-range mobile device. If you're above those, the five tips above — especially OnPush, signals, and lazy loading — will move the needle.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Three more wins worth knowing
These don't make my "top 5" list because they're more situational, but they're worth keeping in mind for larger apps:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pure pipes over methods in templates. Angular re-evaluates method calls in templates on every change detection cycle. Replace them with pure pipes — Angular caches the result and only recalculates when the input changes.&lt;br&gt;
Avoid large object bindings in templates. Binding [data]="largeObject" forces deep comparison on every cycle. Flatten or memoize the values you actually need in the template.&lt;br&gt;
Use the async pipe instead of manual subscriptions. It handles subscription teardown automatically and integrates cleanly with ChangeDetectionStrategy.OnPush — the component re-renders only when the observable emits, not on every cycle.&lt;br&gt;
// ❌ Method call in template — runs every cycle&lt;br&gt;
{{ formatDate(item.createdAt) }}&lt;/p&gt;

&lt;p&gt;// ✅ Pure pipe — cached, runs only when input changes&lt;br&gt;
{{ item.createdAt | formatDate }}&lt;/p&gt;

&lt;p&gt;// ✅ async pipe with OnPush&lt;br&gt;
@Component({&lt;br&gt;
  changeDetection: ChangeDetectionStrategy.OnPush,&lt;br&gt;
  template: &lt;code&gt;&amp;lt;div *ngIf="data$ | async as data"&amp;gt;{{ data.name }}&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;br&gt;
})&lt;br&gt;
export class MyComponent {&lt;br&gt;
  data$ = this.service.getData();&lt;br&gt;
}&lt;br&gt;
Angular 17 performance checklist&lt;br&gt;
A quick reference I run through before shipping any Angular feature to production:&lt;/p&gt;

&lt;p&gt;✅ ChangeDetectionStrategy.OnPush on every component&lt;br&gt;
✅ Signals for local and shared state — no unnecessary BehaviorSubjects&lt;br&gt;
✅ &lt;a class="mentioned-user" href="https://dev.to/for"&gt;@for&lt;/a&gt; with track on every list&lt;br&gt;
✅ CdkVirtualScrollViewport on lists over 50 items&lt;br&gt;
✅ All routes lazy-loaded with loadComponent&lt;br&gt;
✅ Pure pipes instead of template method calls&lt;br&gt;
✅ async pipe for observable bindings&lt;br&gt;
✅ Bundle analysed — no accidental large imports in initial chunk&lt;br&gt;
✅ Lighthouse mobile score above 85 before release&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://jaklens.com/blog/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;jaklens.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>angular</category>
      <category>frontend</category>
      <category>performance</category>
      <category>typescript</category>
    </item>
    <item>
      <title>AI Invoice OCR Explained: How Local AI Reads Your PDFs</title>
      <dc:creator>Jak s</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:14:21 +0000</pubDate>
      <link>https://dev.to/jak_s_765bff302f0b7674066/ai-invoice-ocr-explained-how-local-ai-reads-your-pdfs-3671</link>
      <guid>https://dev.to/jak_s_765bff302f0b7674066/ai-invoice-ocr-explained-how-local-ai-reads-your-pdfs-3671</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://jaklens.com/blog/ai-invoice-ocr-explained" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;jaklens.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
Step 1 in depth: pdfjs-dist&lt;br&gt;
pdfjs-dist is Mozilla's PDF rendering library — the same engine that powers Firefox's built-in PDF viewer. In jaklens.ai, it runs in the Node.js process (via Electron's main process) to extract text content from each page of the invoice.

&lt;p&gt;For a typical digital invoice PDF (generated by Stripe, PayPal, a CRM, or invoicing software), pdfjs produces clean Unicode text that preserves line structure. The output looks something like:&lt;/p&gt;

&lt;p&gt;INVOICE&lt;br&gt;
Invoice #: INV-2024-0891&lt;br&gt;
Date: 15 March 2025&lt;br&gt;
Due Date: 15 April 2025&lt;/p&gt;

&lt;p&gt;Bill To:&lt;br&gt;
Acme Corp Ltd&lt;br&gt;
123 Business Street&lt;/p&gt;

&lt;p&gt;Item          Qty    Unit Price    Amount&lt;br&gt;
Design work   10     $150.00       $1,500.00&lt;br&gt;
Hosting fee    1     $50.00          $50.00&lt;/p&gt;

&lt;p&gt;Subtotal                           $1,550.00&lt;br&gt;
Tax (15%)                            $232.50&lt;br&gt;
TOTAL                              $1,782.50&lt;br&gt;
For scanned PDFs (photographed or printed-and-scanned invoices), pdfjs renders the page to a bitmap, which is then processed by an OCR layer before the text reaches the LLM. This two-pass approach handles the majority of real-world invoice formats.&lt;/p&gt;

&lt;p&gt;Step 2 in depth: Qwen2.5 1.5B via llama.cpp&lt;br&gt;
Qwen2.5 is a language model family from Alibaba DAMO Academy. The 1.5B parameter variant, when quantized to 4-bit GGUF format, fits comfortably in approximately 1.2 GB of RAM and produces fast responses even on consumer CPUs.&lt;/p&gt;

&lt;p&gt;jaklens.ai uses node-llama-cpp, a high-quality Node.js binding for llama.cpp. llama.cpp is the industry-standard C++ inference engine for running GGUF models locally — it supports AVX2/AVX512 CPU acceleration, NVIDIA CUDA, AMD ROCm, and Vulkan.&lt;/p&gt;

&lt;p&gt;The prompt sent to the model is carefully structured to maximize extraction accuracy:&lt;/p&gt;

&lt;p&gt;System prompt: instructs the model to act as an invoice data extractor and return only valid JSON&lt;br&gt;
User message: the raw text from pdfjs, with a schema for the expected output fields&lt;br&gt;
Temperature: set low (0.1–0.2) to reduce hallucination and maximize consistency&lt;br&gt;
Max tokens: constrained to avoid excessive output&lt;br&gt;
The model returns structured JSON similar to:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "vendor": "Design Studio Ltd",&lt;br&gt;
  "invoice_number": "INV-2024-0891",&lt;br&gt;
  "date": "2025-03-15",&lt;br&gt;
  "due_date": "2025-04-15",&lt;br&gt;
  "currency": "USD",&lt;br&gt;
  "subtotal": 1550.00,&lt;br&gt;
  "tax": 232.50,&lt;br&gt;
  "total": 1782.50,&lt;br&gt;
  "line_items": [&lt;br&gt;
    { "description": "Design work", "qty": 10, "unit": 150.00, "amount": 1500.00 },&lt;br&gt;
    { "description": "Hosting fee", "qty": 1, "unit": 50.00, "amount": 50.00 }&lt;br&gt;
  ]&lt;br&gt;
}&lt;br&gt;
All of this inference happens on your hardware. Typical response times range from 3–8 seconds on a modern 8-core CPU, or under 2 seconds with GPU acceleration.&lt;/p&gt;

&lt;p&gt;Why Qwen2.5 for invoices?&lt;br&gt;
Several factors make Qwen2.5 1.5B well-suited for invoice parsing:&lt;/p&gt;

&lt;p&gt;Multilingual.&lt;br&gt;
Handles English and Arabic invoice text natively — important for Middle Eastern markets&lt;br&gt;
Small but capable.&lt;br&gt;
1.5B parameters in 4-bit GGUF is ~1.2 GB — fits on budget hardware&lt;br&gt;
JSON instruction following.&lt;br&gt;
Qwen2.5 is specifically trained for structured output tasks&lt;br&gt;
Free.&lt;br&gt;
Open-weight model, no API costs, no rate limits, no usage tracking&lt;br&gt;
Accuracy and limitations&lt;br&gt;
No OCR system is perfect. Known limitations of the current pipeline:&lt;/p&gt;

&lt;p&gt;Low-quality scans:&lt;br&gt;
Heavily skewed, blurry, or low-DPI scans produce degraded text extraction, which reduces parsing accuracy&lt;br&gt;
Unusual layouts:&lt;br&gt;
Invoices with non-standard structures (tables in images, rotated text, watermarks) may miss fields&lt;br&gt;
Currency ambiguity:&lt;br&gt;
Multi-currency invoices may need manual correction&lt;br&gt;
Hallucination risk:&lt;br&gt;
Like all LLMs, Qwen2.5 can occasionally invent fields not present in the source. Always verify critical totals before confirming&lt;br&gt;
jaklens.ai addresses this by showing all extracted fields in an editable review screen before saving. You confirm, edit, or reject the AI's extraction — keeping humans in control of the data.&lt;/p&gt;

&lt;p&gt;The privacy advantage of local inference&lt;br&gt;
Your invoice text never leaves your machine. It goes from your PDF to your CPU to your SQLite database — entirely within your Windows user session.&lt;br&gt;
Cloud invoice OCR services (including Google Document AI, AWS Textract, and accounting software AI features) send your document to a remote API. That means your vendors, amounts, dates, and financial relationships are processed on someone else's infrastructure. With local llama.cpp inference, that pathway doesn't exist.&lt;/p&gt;

&lt;p&gt;Invoice OCR AI — Frequently Asked Questions&lt;br&gt;
What is invoice OCR AI?&lt;br&gt;
Invoice OCR AI is the use of optical character recognition combined with artificial intelligence (typically large language models) to automatically extract structured data — vendor, amount, date, line items — from invoice documents. Modern invoice OCR AI uses computer vision and machine learning instead of brittle regex templates.&lt;/p&gt;

&lt;p&gt;How does invoice OCR machine learning work?&lt;br&gt;
The invoice OCR machine learning pipeline has three stages. First, a PDF parser like pdfjs-dist extracts raw text from the document. Second, a language model like Qwen2.5 reads that text and identifies which words mean "vendor", "total", "invoice number", etc. Third, the structured JSON output is saved to a database. jaklens.ai runs all three stages locally using llama.cpp.&lt;/p&gt;

&lt;p&gt;Can I run invoice OCR with Node.js?&lt;br&gt;
Yes. Node OCR invoice processing is possible using libraries like pdfjs-dist (Mozilla's PDF parser for Node) for text extraction, and node-llama-cpp for running open-source LLMs locally. This is exactly the stack jaklens.ai uses — a pure JavaScript/Node pipeline with no external API calls.&lt;/p&gt;

&lt;p&gt;What is computer vision invoice extraction?&lt;br&gt;
Computer vision invoice extraction refers to OCR systems that read scanned image invoices (JPEG, PNG, photos) rather than digital PDFs. These pipelines typically use models like Tesseract, PaddleOCR, or vision-language models (VLMs) to convert pixels into text, then feed that text into a language model for field extraction.&lt;/p&gt;

&lt;p&gt;Is invoice OCR deep learning more accurate than rule-based systems?&lt;br&gt;
Yes, significantly. Rule-based invoice OCR breaks the moment a vendor changes their invoice layout. Invoice OCR deep learning models like Qwen2.5 understand context — they can identify a total even if it's labeled "Amount Due", "Grand Total", or "Total Payable". The tradeoff is occasional hallucination, which is why jaklens.ai always shows extracted fields in an editable review screen.&lt;/p&gt;

&lt;p&gt;What AI model is best for invoice OCR in 2026?&lt;br&gt;
For local invoices OCR processing AI, Qwen2.5 1.5B is currently the best balance of size, speed, and accuracy. It runs on consumer CPUs via llama.cpp, fits in ~1.2 GB as a 4-bit GGUF, follows JSON output instructions reliably, and supports both English and Arabic. Larger models like Qwen2.5 7B or Llama 3.1 8B are more accurate but require more RAM.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ocr</category>
      <category>programming</category>
    </item>
    <item>
      <title>Free offline AI invoice tracker — built for Windows.</title>
      <dc:creator>Jak s</dc:creator>
      <pubDate>Wed, 10 Jun 2026 09:27:51 +0000</pubDate>
      <link>https://dev.to/jak_s_765bff302f0b7674066/free-offline-ai-invoicetracker-built-forwindows-549j</link>
      <guid>https://dev.to/jak_s_765bff302f0b7674066/free-offline-ai-invoicetracker-built-forwindows-549j</guid>
      <description>&lt;p&gt;jaklens.ai scans invoices with a local AI model, organizes every expense and income transaction, and lets you query your financial history through a private on-device assistant — running entirely on your Windows PC.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0rjwdj99wezltmo1if1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0rjwdj99wezltmo1if1.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgywj8wj90xt9ikcis5o6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgywj8wj90xt9ikcis5o6.png" alt=" " width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jaklens.com/" rel="noopener noreferrer"&gt;https://jaklens.com/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://jaklens.com/blog/" rel="noopener noreferrer"&gt;https://jaklens.com/blog/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I built 26 free HTML templates across 12 industries — no npm, no build step, just open and ship</title>
      <dc:creator>Jak s</dc:creator>
      <pubDate>Thu, 04 Jun 2026 16:17:20 +0000</pubDate>
      <link>https://dev.to/jak_s_765bff302f0b7674066/i-built-26-free-html-templates-across-12-industries-no-npm-no-build-step-just-open-and-ship-4acj</link>
      <guid>https://dev.to/jak_s_765bff302f0b7674066/i-built-26-free-html-templates-across-12-industries-no-npm-no-build-step-just-open-and-ship-4acj</guid>
      <description>&lt;p&gt;I've been building client projects for years and kept running into the &lt;br&gt;
same problem: free templates either require a full framework setup or &lt;br&gt;
look like they were made in 2015.&lt;/p&gt;

&lt;p&gt;So I spent the last few months building my own collection from scratch.&lt;/p&gt;

&lt;p&gt;What I built&lt;/p&gt;

&lt;p&gt;26 free HTML templates across 12 industries:&lt;/p&gt;

&lt;p&gt;-E-commerce (fashion store, Indian boutique, luxury perfume, tech shop)&lt;br&gt;
-SaaS &amp;amp; App landing pages&lt;br&gt;
-Agency &amp;amp; marketing&lt;br&gt;
-Business &amp;amp; startup&lt;br&gt;
-Restaurant &amp;amp; fine dining&lt;br&gt;
-Medical &amp;amp; dental&lt;br&gt;
-Real estate&lt;br&gt;
-Education &amp;amp; LMS&lt;br&gt;
-Fitness &amp;amp; yoga&lt;br&gt;
-Photography portfolio&lt;br&gt;
-Developer portfolio&lt;br&gt;
-Blog&lt;/p&gt;

&lt;p&gt;Every template is a single .html file. CSS and JS are inlined. &lt;/p&gt;

&lt;p&gt;No Node.js. No npm install. No Webpack. No Vite. Just open the file &lt;br&gt;
in a browser and it works.&lt;/p&gt;

&lt;p&gt;Built with Tailwind CSS (via CDN) and vanilla JavaScript.&lt;/p&gt;

&lt;p&gt;A few highlights&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;L'Élixir Perfume Store&lt;/strong&gt; has a cinematic intro screen, &lt;br&gt;
physics-based floating scent bubbles, a canvas particle spritz effect, &lt;br&gt;
and a personality quiz — all in one HTML file.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;Amara Indian Boutique&lt;/strong&gt; has a full cart drawer, quick-view modal, &lt;br&gt;
size guide, and custom consultation form.&lt;/p&gt;

&lt;p&gt;Tech used&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tailwind CSS v3 (CDN)&lt;/li&gt;
&lt;li&gt;Vanilla JavaScript&lt;/li&gt;
&lt;li&gt;Lucide Icons&lt;/li&gt;
&lt;li&gt;Google Fonts&lt;/li&gt;
&lt;li&gt;HTML5 Canvas for animations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where to find it&lt;/p&gt;

&lt;p&gt;Live gallery: &lt;a href="https://templates.jack-codes.com" rel="noopener noreferrer"&gt;https://templates.jack-codes.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
 GitHub: &lt;a href="https://github.com/jakscoduxdev-netizen/website_templates" rel="noopener noreferrer"&gt;https://github.com/jakscoduxdev-netizen/website_templates&lt;/a&gt;&lt;br&gt;&lt;br&gt;
All MIT licensed. Free for personal and commercial use.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about how any of them were built.&lt;/p&gt;

</description>
      <category>html</category>
      <category>resources</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
