<?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: Aleksandar Maletic</title>
    <description>The latest articles on DEV Community by Aleksandar Maletic (@maleta).</description>
    <link>https://dev.to/maleta</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F277166%2F59b2beae-1758-43f6-863f-53399a2bb5d6.jpg</url>
      <title>DEV Community: Aleksandar Maletic</title>
      <link>https://dev.to/maleta</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maleta"/>
    <language>en</language>
    <item>
      <title>Meet BlokJS - 9 KB, No Build Step, Standalone, Full FE Framework</title>
      <dc:creator>Aleksandar Maletic</dc:creator>
      <pubDate>Mon, 02 Mar 2026 20:32:12 +0000</pubDate>
      <link>https://dev.to/maleta/meet-blokjs-9-kb-no-build-step-standalone-full-fe-framework-3gfg</link>
      <guid>https://dev.to/maleta/meet-blokjs-9-kb-no-build-step-standalone-full-fe-framework-3gfg</guid>
      <description>&lt;h2&gt;
  
  
  BlokJS - Zero-Build, Zero-Dependency, Standalone, Reactive, Lightweight UI Framework
&lt;/h2&gt;

&lt;p&gt;New project, new frontend. Install Node. Pick a bundler. Configure TypeScript. Install a router package. Install a state management package. Set up hot reload. Debug the config. Twenty minutes later you still haven't written a single line of UI code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;BlokJS&lt;/strong&gt; is a reactive UI framework that skips all of that.&lt;/p&gt;

&lt;p&gt;One &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag. 9 KB gzipped. Zero dependencies. No virtual DOM, no JSX, no template compiler. Your views are plain JavaScript objects - the browser runs them directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@maleta/blokjs/dist/blokjs.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's your entire setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Philosophy
&lt;/h2&gt;

&lt;p&gt;BlokJS is built around a few ideas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No build step required.&lt;/strong&gt; A framework that needs a compiler before it can run has already added complexity. BlokJS app is plain JavaScript object - it run directly in the browser. You can still use a bundler if you want to have code well organized in files , but it's never a requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Batteries-included, not batteries-heavy.&lt;/strong&gt; Reactive state, components, routing, stores, and async tracking are all built in. One 9 KB package instead of assembling five separate libraries. But "included" doesn't mean "bloated" - the goal is to cover common needs without unnecessary weight.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simplicity over cleverness.&lt;/strong&gt; The API surface is small on purpose. There are no special directives, no lifecycle alphabet soup, no framework-specific syntax to learn. If you know JavaScript objects and functions, you already know most of the BlokJS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Direct DOM updates, no virtual DOM.&lt;/strong&gt; Instead of diffing a virtual tree and patching the real DOM, BlokJS tracks exactly which state each binding depends on and updates only that binding when the state changes. This avoids the overhead of a full diff/patch cycle, making targeted updates fast and predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Counter in 15 Lines
&lt;/h2&gt;

&lt;p&gt;Let's start with the most basic example - a counter. No install, no config, no build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@maleta/blokjs/dist/blokjs.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;blok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nf"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dec&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. &lt;code&gt;state&lt;/code&gt; is reactive. When &lt;code&gt;count&lt;/code&gt; changes, the &lt;code&gt;h1&lt;/code&gt; updates automatically. The &lt;code&gt;$&lt;/code&gt; proxy creates reactive references that the framework resolves at render time. No &lt;code&gt;useState&lt;/code&gt;, no &lt;code&gt;useEffect&lt;/code&gt;, no &lt;code&gt;ref()&lt;/code&gt;. Just state and a view.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Objects Instead of JSX or Templates?
&lt;/h2&gt;

&lt;p&gt;Instead of &lt;code&gt;&amp;lt;div class="card"&amp;gt;&lt;/code&gt; or &lt;code&gt;React.createElement('div')&lt;/code&gt;, BlokJS uses plain objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This...&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ...renders this:&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;div class="card"&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//   &amp;lt;h1&amp;gt;My Title&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;//   &amp;lt;p&amp;gt;Some description&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// &amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why? Because it's just JavaScript. No parser. No compiler. No template language to learn. You can refactor with standard tools, and there's nothing between your code and the browser.&lt;/p&gt;

&lt;p&gt;The key elements of the view DSL:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;text&lt;/code&gt;&lt;/strong&gt; - text content, static or reactive (&lt;code&gt;{ text: $.name }&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;children&lt;/code&gt;&lt;/strong&gt; - array of child elements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;class&lt;/code&gt;&lt;/strong&gt; - string, object, or array (&lt;code&gt;{ class: { active: $.isActive } }&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;model&lt;/code&gt;&lt;/strong&gt; - two-way binding for inputs (&lt;code&gt;{ input: { model: $.search } }&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;when&lt;/code&gt;&lt;/strong&gt; - conditional rendering (&lt;code&gt;{ when: $.isVisible, children: [...] }&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;each&lt;/code&gt;&lt;/strong&gt; - list rendering (&lt;code&gt;{ each: $.items, as: 'item', children: [...] }&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Events&lt;/strong&gt; - just the event name as key (&lt;code&gt;{ button: { click: 'save' } }&lt;/code&gt;) with optional arguments (&lt;code&gt;{ click: 'remove(item)' }&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Negation works too. &lt;code&gt;$.not.isLoggedIn&lt;/code&gt; evaluates to &lt;code&gt;true&lt;/code&gt; when &lt;code&gt;isLoggedIn&lt;/code&gt; is falsy. No ternaries, no &lt;code&gt;!&lt;/code&gt; operators in templates.&lt;/p&gt;




&lt;h2&gt;
  
  
  Components - Not a Toy
&lt;/h2&gt;

&lt;p&gt;BlokJS has a component system with props, events, slots, and lifecycle hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;blok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;component&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;TodoItem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;li&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;button&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use it in a parent by name. Pass props, listen to events with the &lt;code&gt;on_&lt;/code&gt; prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;each&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;todo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;on_remove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;handleRemove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Components also support &lt;strong&gt;slots&lt;/strong&gt; for content projection, &lt;strong&gt;computed properties&lt;/strong&gt;, &lt;strong&gt;watchers&lt;/strong&gt;, and &lt;code&gt;mount&lt;/code&gt;/&lt;code&gt;unmount&lt;/code&gt; lifecycle hooks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stores - Global State with Automatic Async Tracking
&lt;/h2&gt;

&lt;p&gt;Most frameworks require you to manually wire up loading spinners and error messages for every async operation. BlokJS handles this automatically.&lt;/p&gt;

&lt;p&gt;Define a store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;blok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;logout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in your template, you get &lt;code&gt;loading&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; on the spot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Loading spinner - appears automatically while login() runs&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Signing in...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Error message - appears automatically if login() throws&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Logged in state&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any method that returns a Promise is tracked. The framework wraps it, sets &lt;code&gt;loading.methodName = true&lt;/code&gt;, and if it throws, captures the error into &lt;code&gt;error.methodName&lt;/code&gt;. You just bind it. This works for both store methods and component methods.&lt;/p&gt;




&lt;h2&gt;
  
  
  Routing - Built In
&lt;/h2&gt;

&lt;p&gt;No separate router package. Routes, dynamic params, guards, hash and history modes are all included:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;blok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/product/:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ProductDetail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;guard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;requireAuth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;NotFound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;

  &lt;span class="na"&gt;guards&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;requireAuth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Admin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside components, access route data via &lt;code&gt;this.route.params&lt;/code&gt;, &lt;code&gt;this.route.query&lt;/code&gt;, and navigate programmatically with &lt;code&gt;this.navigate('/path')&lt;/code&gt;. Guards can return &lt;code&gt;true&lt;/code&gt; (allow), &lt;code&gt;false&lt;/code&gt; (block), or a redirect path string.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;p&gt;I wrote about &lt;a href="https://dev.to/maleta/cors-xss-and-csrf-with-examples-in-10-minutes-35k3"&gt;CORS, XSS and CSRF&lt;/a&gt; before, and web security was on my mind when building BlokJS.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;text&lt;/code&gt; binding is always safe - it uses &lt;code&gt;textContent&lt;/code&gt;, so HTML in user input is rendered as literal text, not parsed. The &lt;code&gt;html&lt;/code&gt; binding sets &lt;code&gt;innerHTML&lt;/code&gt; directly without sanitization, same as Vue's &lt;code&gt;v-html&lt;/code&gt;. If you need to render HTML, make sure you trust the source - never pass unsanitized user input to &lt;code&gt;html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;URL attributes (&lt;code&gt;href&lt;/code&gt;, &lt;code&gt;src&lt;/code&gt;, &lt;code&gt;action&lt;/code&gt;) are validated - &lt;code&gt;javascript:&lt;/code&gt; and &lt;code&gt;data:&lt;/code&gt; URIs are blocked.&lt;/p&gt;




&lt;h2&gt;
  
  
  What BlokJS is Not
&lt;/h2&gt;

&lt;p&gt;BlokJS is not trying to replace React, Angular or Vue for enterprise dashboards with hundreds of components. It doesn't have SSR yet - though since views are plain objects rather than DOM-dependent templates, server-side rendering is a natural future addition. It doesn't have a virtual DOM (by design - fine-grained reactivity means direct DOM updates, which works well for most use cases).&lt;/p&gt;

&lt;p&gt;Where it fits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prototypes&lt;/strong&gt; and MVPs where you want something running quick&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal tools&lt;/strong&gt; and admin panels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small to medium apps&lt;/strong&gt; - todo lists, dashboards, forms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learning&lt;/strong&gt; - the entire API fits in your head in an afternoon&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding&lt;/strong&gt; - drop reactive UI into any existing page&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  LLM-Friendly by Design
&lt;/h2&gt;

&lt;p&gt;BlokJS ships with a dedicated &lt;a href="https://github.com/maleta/blokjs/blob/main/packages/blokjs/llm-reference.md" rel="noopener noreferrer"&gt;LLM reference document&lt;/a&gt; - a condensed version of the entire API optimized for LLM consumption.&lt;/p&gt;

&lt;p&gt;The document is &lt;strong&gt;~2,900 tokens&lt;/strong&gt; (measured using &lt;a href="https://platform.claude.com/docs/en/build-with-claude/token-counting" rel="noopener noreferrer"&gt;Anthropic's official token counting API&lt;/a&gt;). That's small enough to paste into any LLM's context window - less than 1.5% of a 200K context window.&lt;/p&gt;

&lt;p&gt;The idea: include it in your prompt, and an LLM can write working BlokJS code with accurate API usage. The reference is part of the npm package (&lt;code&gt;llm-reference.md&lt;/code&gt;), so it's always available alongside the framework itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CDN (zero setup):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/@maleta/blokjs/dist/blokjs.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without a bundler, you have full control over what loads and when. Split components into separate files, load them conditionally, defer what isn't needed on first render - the browser handles it natively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @maleta/blokjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;With Vite:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create blokjs my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npx vite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Vite plugin adds automatic component and store registration from file directories, bundled output, and hot module replacement during development.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://maleta.github.io/blokjs/" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/maleta/blokjs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@maleta/blokjs" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/maleta/blokjs/blob/main/packages/blokjs/llm-reference.md" rel="noopener noreferrer"&gt;LLM Reference (~2,900 tokens)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;BlokJS is MIT licensed. If you try it, I'd love to hear what you build.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Transforming Internet Browsing Experience on Desktop: A Proof of Concept</title>
      <dc:creator>Aleksandar Maletic</dc:creator>
      <pubDate>Wed, 05 Jun 2024 20:23:56 +0000</pubDate>
      <link>https://dev.to/maleta/transforming-desktop-browsing-experience-a-proof-of-concept-417</link>
      <guid>https://dev.to/maleta/transforming-desktop-browsing-experience-a-proof-of-concept-417</guid>
      <description>&lt;p&gt;Over time, desktop internet browsing can become clumsy due to the accumulation of numerous open tabs. You may become attached to tabs you've opened or you forget to close them, and you end up carrying them over to each new browsing session. This issue has been recognized in recent years, and some solutions have been developed to enhance the browsing experience.&lt;/p&gt;

&lt;p&gt;The most valuable solution for me was the introduction of &lt;a href="https://blog.google/products/chrome/manage-tabs-with-google-chrome/" rel="noopener noreferrer"&gt;tab grouping in Chrome&lt;/a&gt;. It helped keep my tabs organized. Before this, there were similar tools to tackle this problem, such as Session Buddy, a Chrome extension that allows you to store and manage sessions effortlessly.&lt;/p&gt;

&lt;p&gt;However, both solutions share a common drawback: web browsing now requires time for tab management. While the time involved isn't substantial, it disrupts the browsing flow, requiring a cleanup after each session or cleaning opened tabs during browsing, and that is not fun.&lt;/p&gt;

&lt;p&gt;After my last browsing session cleanup, I reflected on how having so many tabs open had become normal. I realized that the web browsing experience hasn't fundamentally changed since &lt;a href="https://en.wikipedia.org/wiki/NetCaptor" rel="noopener noreferrer"&gt;the introduction of tabs&lt;/a&gt;. When opening a link, you either &lt;strong&gt;replace the current page&lt;/strong&gt; or &lt;strong&gt;open in a new tab&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if&lt;/strong&gt; there was a way to open a link in the same position as the link itself, without replacing the current page?&lt;/p&gt;

&lt;p&gt;This approach should lead to fewer open tabs per session and provide a smoother browsing experience, as your eyes wouldn't need to readjust to a new position.&lt;/p&gt;

&lt;p&gt;To test this browsing experience, I built a simple Chrome extension (&lt;a href="https://github.com/maleta/link-in-same-page" rel="noopener noreferrer"&gt;source&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Here's a preview of how it works:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmaleta%2Flink-in-same-page%2Fblob%2Fmaster%2Fdemo1-short.webp%3Fraw%3Dtrue" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fmaleta%2Flink-in-same-page%2Fblob%2Fmaster%2Fdemo1-short.webp%3Fraw%3Dtrue" alt="demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The extension works partially due to &lt;a href="https://github.com/maleta/link-in-same-page?tab=readme-ov-file#security-concerns" rel="noopener noreferrer"&gt;multiple security concerns&lt;/a&gt; and the fact that it utilizes the &lt;code&gt;iframe&lt;/code&gt; HTML element. If this browsing experience is properly integrated, users should have an isolated and safe browsing experience.&lt;/p&gt;

&lt;p&gt;Feel free to share your thoughts or suggestions.&lt;/p&gt;

</description>
      <category>ux</category>
      <category>webbrowser</category>
    </item>
    <item>
      <title>CORS, XSS and CSRF with examples in 10 minutes</title>
      <dc:creator>Aleksandar Maletic</dc:creator>
      <pubDate>Mon, 23 Dec 2019 11:15:21 +0000</pubDate>
      <link>https://dev.to/maleta/cors-xss-and-csrf-with-examples-in-10-minutes-35k3</link>
      <guid>https://dev.to/maleta/cors-xss-and-csrf-with-examples-in-10-minutes-35k3</guid>
      <description>&lt;p&gt;This article should be your entry point for existing web security standards, most common web attacks and the methods to prevent them. At the end, you will also find out how and why Samy was everyone's hero.(except Rupert Murdoch's, I guess)&lt;/p&gt;

&lt;h1&gt;
  
  
  CORS
&lt;/h1&gt;

&lt;p&gt;Cross-origin resource sharing, or CORS, is security feature of &lt;a href="https://caniuse.com/#search=CORS" rel="noopener noreferrer"&gt;IE10+, Chrome 4+, Firefox 3.5+ or almost every version of browser released after 2012 except Opera Mini.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When CORS are &lt;em&gt;configured&lt;/em&gt; on server that is available on domain &lt;code&gt;website.com&lt;/code&gt; then resources from that domain those are requested trough AJAX must be initiated from assets that is served from that same domain. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmdn.mozillademos.org%2Ffiles%2F14295%2FCORS_principle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmdn.mozillademos.org%2Ffiles%2F14295%2FCORS_principle.png" alt="CORS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the other words, if we enable CORS on &lt;code&gt;domain-b.com&lt;/code&gt; and configure it to allow only &lt;code&gt;GET&lt;/code&gt; requests from domain &lt;code&gt;domain-b.com&lt;/code&gt; then if you want to use image available under &lt;code&gt;https://domain-b.com/images/example.png&lt;/code&gt; in canvas on your website which is hosted on &lt;code&gt;domain-a.com&lt;/code&gt;, than that image will not be loaded for most of your visitors. &lt;br&gt;
Your resources protected by CORS will still be available when requested by any tool or browser that doesn't respect &lt;code&gt;CORS policy&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CORS configuration
&lt;/h3&gt;

&lt;p&gt;CORS are &lt;strong&gt;disabled by default&lt;/strong&gt; which means that there is no adequate server handler that will configure CORS which means that you cannot access resources from different origin in your XHR. Basically, if you &lt;em&gt;don't do anything&lt;/em&gt; or specifically enable CORS only for specific domains, then any AJAX request trying to access your resources will be rejected because web browsers are respectful to the &lt;code&gt;CORS policy&lt;/code&gt;.&lt;br&gt;
This is the reason why you encounter &lt;em&gt;CORS issue&lt;/em&gt; when you start developing SPA using VueJS and NodeJS. Your VueJS application is hosted on &lt;code&gt;http://localhost:8080&lt;/code&gt; and when you try to access NodeJS server application on &lt;code&gt;http://localhost:8000&lt;/code&gt; you get "&lt;code&gt;No Access-Control-Allow-Origin header is present&lt;/code&gt;" because those are two different &lt;code&gt;ORIGINS&lt;/code&gt;(combination of &lt;code&gt;PROTOCOL&lt;/code&gt;, &lt;code&gt;HOST&lt;/code&gt; and &lt;code&gt;PORT&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;Cool fix for CORS issue in VueJS development mode is to set devServer proxy in your &lt;code&gt;vue.config.js&lt;/code&gt; file as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;devServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To setup CORS in production you should add appropriate listener for &lt;code&gt;OPTIONS&lt;/code&gt; request. That listener should send response &lt;code&gt;200&lt;/code&gt; with &lt;code&gt;no body&lt;/code&gt; but with &lt;code&gt;Headers&lt;/code&gt; that will define your wanted &lt;em&gt;CORS policy&lt;/em&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Access-Control-Allow-Origin: https://domain-b.com
Access-Control-Allow-Methods: GET


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For more info on how to configure CORS check &lt;a href="https://enable-cors.org/index.html" rel="noopener noreferrer"&gt;https://enable-cors.org/index.html&lt;/a&gt;, and to dive deeper in &lt;code&gt;CORS policy&lt;/code&gt;check &lt;a href="https://livebook.manning.com/book/cors-in-action/part-1/" rel="noopener noreferrer"&gt;https://livebook.manning.com/book/cors-in-action/part-1/&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  XSS
&lt;/h1&gt;

&lt;p&gt;XSS stands for Cross Site Scripting and it is injection type of attack. It is listed as 7th out of top 10 vulnerabilities identified by OWASP in 2017. Cross site scripting is the method where the attacker injects malicious script into trusted website.(section updated, thanks Sandor) There are 3 types of such attacks.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stored XSS - Vulnerability coming from &lt;strong&gt;unprotected and not sanitized&lt;/strong&gt; user inputs those are directly stored in database and displayed to other users&lt;/li&gt;
&lt;li&gt;Reflected XSS - Vulnerability coming from &lt;strong&gt;unprotected and not sanitized&lt;/strong&gt; values from URLs those are directly used in web pages&lt;/li&gt;
&lt;li&gt;DOM based XSS - Similar as reflected XSS, &lt;strong&gt;unprotected and not sanitized&lt;/strong&gt; values from URLs used directly in web pages, with difference that DOM based XSS doesn't even go to server side&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Attack
&lt;/h3&gt;

&lt;p&gt;1. Stored XSS&lt;/p&gt;

&lt;p&gt;Here is an example of attack. The attacker comes on your website and finds unprotected input field such as comment field or user name field and enters malicious script instead of expected value. After that, whenever that value should be displayed to other users it will execute malicious code. Malicious script can try to access your account on other websites, can be the involved in DDoS attack or similar. Visual representation(source geeksforgeeks.org):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.geeksforgeeks.org%2Fwp-content%2Fuploads%2F20190516152959%2FCross-Site-ScriptingXSS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.geeksforgeeks.org%2Fwp-content%2Fuploads%2F20190516152959%2FCross-Site-ScriptingXSS.png" alt="XSS example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2. Reflected XSS&lt;/p&gt;

&lt;p&gt;Reflected XSS is attack that happens when attacker discovers page with such vulnerability, for example:&lt;/p&gt;

&lt;p&gt;expected URL: &lt;code&gt;https://mywebpage.com/search?q=javascript&lt;/code&gt;&lt;br&gt;
malicious URL: &lt;code&gt;https://mywebpage.com/search?q=&amp;lt;script&amp;gt;alert('fortunately, this will not work!')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt; showing results for keyword 
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;q=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
...
...JavaScript results...
...
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After discovery, attacker baits user to click on such malicious URL and voila. User sensitive data is exploited. &lt;/p&gt;

&lt;p&gt;Lifecycle of attack ilustrated in example provided by geekforgeeks.com:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.geeksforgeeks.org%2Fwp-content%2Fuploads%2F20190516153002%2FreflectedXSS.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmedia.geeksforgeeks.org%2Fwp-content%2Fuploads%2F20190516153002%2FreflectedXSS.png" alt="Reflected XSS example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3. DOM based XSS&lt;/p&gt;

&lt;p&gt;This kind of attack is same as reflected but with difference that malicious &lt;code&gt;URL&lt;/code&gt; part will not be sent to server at all. For above example:&lt;/p&gt;

&lt;p&gt;expected URL: &lt;code&gt;https://mywebpage.com/search?q=javascript&lt;/code&gt;&lt;br&gt;
malicious URL(reflected XSS): &lt;code&gt;https://mywebpage.com/search?q=&amp;lt;script&amp;gt;alert('fortunately, this will not work!')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;br&gt;
malicious URL(DOM based XSS): &lt;code&gt;https://mywebpage.com/search#q=&amp;lt;script&amp;gt;alert('fortunately, this will not work!')&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Difference is in the character &lt;code&gt;#&lt;/code&gt; being used instead of &lt;code&gt;?&lt;/code&gt;. The browsers &lt;strong&gt;do not send part of URL after # to server&lt;/strong&gt; so they pass it directly to your client code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Protection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Every value&lt;/strong&gt; that can be entered by user and is used in your app(either on server side either on client side) should be treated as &lt;strong&gt;untrusted data&lt;/strong&gt; and therefore should be &lt;strong&gt;processed before using&lt;/strong&gt;! You should do safety check in your server app and your client app, as well!&lt;br&gt;
As &lt;a href="https://vuejs.org/v2/guide/security.html" rel="noopener noreferrer"&gt;showed in documentation&lt;/a&gt; VueJS by itself escapes string before getting value from variable. Newer versions of Angular also escape strings implicitly, so if you are using Vanilla JS, JQuery or similar you should implement string escape manually.&lt;/p&gt;

&lt;p&gt;There are three most common approaches on &lt;strong&gt;processing untrusted data&lt;/strong&gt; are listed below and ideal method depends on the actual type of the field that you need to process. &lt;/p&gt;

&lt;h4&gt;
  
  
  1. String validation
&lt;/h4&gt;

&lt;p&gt;Validation is the method where user defines set of rules, and demand &lt;em&gt;untrusted data&lt;/em&gt; to satisfy those rules before moving on. This method is good for &lt;em&gt;number values&lt;/em&gt;, &lt;em&gt;username&lt;/em&gt;, &lt;em&gt;email&lt;/em&gt;, &lt;em&gt;password&lt;/em&gt; and similar fields with concrete set of syntax rules.&lt;/p&gt;

&lt;p&gt;Check for existing libraries for your framework before considering to write validators by your own.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. String escape
&lt;/h4&gt;

&lt;p&gt;Escape method is useful for cases where you should enable user to use punctuation marks. This method goes through string and looks for special characters, such as &lt;code&gt;&amp;lt;&lt;/code&gt; &lt;code&gt;&amp;gt;&lt;/code&gt; and replace them with appropriate HTML character entity name. Here is basic function that you could use:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;escapeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;amp;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;amp;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/"/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;quot;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Again, check for existing libraries before writing your own.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. String sanitization
&lt;/h4&gt;

&lt;p&gt;Sanitizing string is used when user is allowed to enter some HTML tags in their comments, articles or similar. The sanitize method goes through text and looks for HTML tags that you specify and removes them. One of most popular libraries that uses this approach is &lt;a href="https://github.com/google/closure-library/blob/master/closure/goog/html/sanitizer/htmlsanitizer.js" rel="noopener noreferrer"&gt;Google Closure&lt;/a&gt;.&lt;br&gt;
This method is resource expensive and considered as harmful, so do more research before choosing it.&lt;/p&gt;

&lt;p&gt;Web browsers(no available sources since what version, IE fixed this issue in 2014.) automatically escape URLs before sending them to server side and making them available in &lt;code&gt;window.location&lt;/code&gt; object also, so 2nd and 3rd type of attack are here just to be aware of them and to make clear that URL params should also be treated as untrusted data. &lt;/p&gt;

&lt;p&gt;For more detailed info about XSS and how to properly protect your application if you rotate a lot of &lt;strong&gt;untrusted data&lt;/strong&gt;, please check &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP cheatsheet on XSS prevention&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  CSRF
&lt;/h1&gt;

&lt;p&gt;Cross site request forgery or CSRF is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user's web browser &lt;strong&gt;to perform an unwanted action on an other trusted site&lt;/strong&gt; where the user is authenticated. This vulnerability is possible when browser automatically sends authorization resource, such as session cookie, IP address or similar with each request.&lt;/p&gt;

&lt;h3&gt;
  
  
  ATTACK
&lt;/h3&gt;

&lt;p&gt;Let's suppose that user is logged in to your unprotected stock exchange web application and that you are using either session cookie either JWT cookie for authentication. Attacker also uses your service and is able to check how your API works. Attacker tricks user to execute script(by clicking on SPAM link in email or similar) that will send request to your API &lt;code&gt;https://www.stockexchange.com/users/withdraw?how_much=all&amp;amp;address=MY_ADDRESS&lt;/code&gt;(terrible API design, don't ask). Since request is executed from browser that sends authentication payload with every request, your stockexchange web server will authenticate user successfully and execute transaction and tricked user will lose all his balance &lt;strong&gt;without even being aware of it&lt;/strong&gt; because everything happened in the background. Visual representation(source miro.medium.com)&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F736%2F1%2A_DGU1bpdqTMKtTNyBc_wRw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F736%2F1%2A_DGU1bpdqTMKtTNyBc_wRw.png" alt="CSRF attack"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  PROTECTION
&lt;/h3&gt;

&lt;p&gt;Luckily there are easy to implement patterns that prevent this web attacks. One of the most common pattern is usage of &lt;code&gt;CSRF token&lt;/code&gt;. Basically procedure is following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate unique token for each user's request, so called &lt;code&gt;CSRF token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Store it safely on server and send it back to user as payload of response.&lt;/li&gt;
&lt;li&gt;Store &lt;code&gt;CSRF token&lt;/code&gt; on client side.&lt;/li&gt;
&lt;li&gt;When user tries to execute any state-changing* request send that &lt;code&gt;CSRF token&lt;/code&gt; with request as a payload.&lt;/li&gt;
&lt;li&gt;Before executing that request on server side check if &lt;code&gt;CSRF token&lt;/code&gt; is present and it is valid.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is the easiest way to prevent CSRF attack for all users.&lt;/p&gt;

&lt;p&gt;If you are are dealing only with visitors that uses &lt;a href="https://caniuse.com/#feat=same-site-cookie-attribute" rel="noopener noreferrer"&gt;modern browsers&lt;/a&gt; then you can rely on &lt;code&gt;SameSite&lt;/code&gt; attribute of session cookie.(thanks Gergely)&lt;/p&gt;

&lt;p&gt;Since server's responses are processable in XHR response, then &lt;strong&gt;there is no protection on CSRF attack if your web application is XSS vulnerable!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To dive deeper check &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;OWASP cheatsheet on CSRF&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  BONUS
&lt;/h2&gt;

&lt;p&gt;Short documentary about Samy, author of worm that took down MySpace back in 2005 abusing XSS vulnerability, passing MySpace's CSRF defense. &lt;br&gt;
&lt;a href="https://youtu.be/DtnuaHl378M" rel="noopener noreferrer"&gt;https://youtu.be/DtnuaHl378M&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More info on Samy's worm &lt;br&gt;
&lt;a href="https://samy.pl/myspace/tech.html" rel="noopener noreferrer"&gt;https://samy.pl/myspace/tech.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>java</category>
    </item>
  </channel>
</rss>
