<?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: Jose Angel Navarro Martinez</title>
    <description>The latest articles on DEV Community by Jose Angel Navarro Martinez (@janmbaco).</description>
    <link>https://dev.to/janmbaco</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%2F3603838%2Fb12e7ba9-cdd9-4bcd-bb0e-e9c48f4ba372.jpg</url>
      <title>DEV Community: Jose Angel Navarro Martinez</title>
      <link>https://dev.to/janmbaco</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/janmbaco"/>
    <language>en</language>
    <item>
      <title>Why I Stopped Putting So Much Logic in Templates</title>
      <dc:creator>Jose Angel Navarro Martinez</dc:creator>
      <pubDate>Mon, 27 Apr 2026 10:48:42 +0000</pubDate>
      <link>https://dev.to/janmbaco/why-i-stopped-putting-so-much-logic-in-templates-5e15</link>
      <guid>https://dev.to/janmbaco/why-i-stopped-putting-so-much-logic-in-templates-5e15</guid>
      <description>&lt;p&gt;I don’t think templates should become hidden execution environments.&lt;/p&gt;

&lt;p&gt;That was one of the main ideas I kept coming back to while building &lt;strong&gt;Pick Components&lt;/strong&gt;, a small Web Components framework I’ve been working on in TypeScript.&lt;/p&gt;

&lt;p&gt;I haven’t been doing TypeScript forever. In fact, part of the reason I started building this was because I wanted to understand the browser, components, templates, decorators, and reactivity from the inside instead of just using another big stack and accepting all of its decisions.&lt;/p&gt;

&lt;p&gt;At first, templates feel like the obvious place to add power.&lt;/p&gt;

&lt;p&gt;You start with:&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;p&amp;gt;&lt;/span&gt;Hello, {{name}}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you want conditions.&lt;/p&gt;

&lt;p&gt;Then loops.&lt;/p&gt;

&lt;p&gt;Then expressions.&lt;/p&gt;

&lt;p&gt;Then event wiring.&lt;/p&gt;

&lt;p&gt;Then helper calls.&lt;/p&gt;

&lt;p&gt;Then more complex logic.&lt;/p&gt;

&lt;p&gt;And after a while, the template is no longer just describing UI. It starts becoming a second programming environment.&lt;/p&gt;

&lt;p&gt;That is where things get uncomfortable for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is not templates
&lt;/h2&gt;

&lt;p&gt;I like templates.&lt;/p&gt;

&lt;p&gt;They are readable. They make UI structure obvious. They are close to HTML, and that matters.&lt;/p&gt;

&lt;p&gt;The problem starts when too much responsibility gets pushed into them.&lt;/p&gt;

&lt;p&gt;At some point, the component becomes harder to reason about because logic is split across too many places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;some in the class&lt;/li&gt;
&lt;li&gt;some in services&lt;/li&gt;
&lt;li&gt;some in event handlers&lt;/li&gt;
&lt;li&gt;some in template expressions&lt;/li&gt;
&lt;li&gt;some hidden behind framework rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That can work, of course. Many frameworks do it well.&lt;/p&gt;

&lt;p&gt;But I wanted a different trade-off.&lt;/p&gt;

&lt;p&gt;I wanted templates to stay useful, but limited.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I ended up with
&lt;/h2&gt;

&lt;p&gt;In Pick Components, I try to keep the template focused on rendering.&lt;/p&gt;

&lt;p&gt;The stronger TypeScript parts stay in TypeScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;state&lt;/li&gt;
&lt;li&gt;actions&lt;/li&gt;
&lt;li&gt;services&lt;/li&gt;
&lt;li&gt;lifecycle&lt;/li&gt;
&lt;li&gt;routing&lt;/li&gt;
&lt;li&gt;domain logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A very small example looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PickComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PickRender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Reactive&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pick-components&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="nd"&gt;PickRender&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello-example&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&amp;lt;p&amp;gt;Hello, {{name}}!&amp;lt;/p&amp;gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloExample&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;PickComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Reactive&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Somebody&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;The template reads the value.&lt;/p&gt;

&lt;p&gt;TypeScript owns the state.&lt;/p&gt;

&lt;p&gt;That sounds simple, but that boundary matters a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I avoided arbitrary JavaScript in templates
&lt;/h2&gt;

&lt;p&gt;One decision I made early was that templates should not run arbitrary JavaScript.&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;eval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No &lt;code&gt;new Function&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No “just execute this string and hope for the best”.&lt;/p&gt;

&lt;p&gt;Pick Components uses a constrained expression model instead. The goal is not to make the template as powerful as TypeScript. The goal is to make it predictable.&lt;/p&gt;

&lt;p&gt;That is a trade-off.&lt;/p&gt;

&lt;p&gt;It means the template cannot do everything.&lt;/p&gt;

&lt;p&gt;But that is also the point.&lt;/p&gt;

&lt;p&gt;When logic becomes important, I want it back in TypeScript, where the tooling, types, imports, refactoring, and errors are stronger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declarative does not have to mean magical
&lt;/h2&gt;

&lt;p&gt;For example, list rendering can still be declarative.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pick-for&lt;/code&gt; exists for that:&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;pick-for&lt;/span&gt; &lt;span class="na"&gt;items=&lt;/span&gt;&lt;span class="s"&gt;"{{users}}"&lt;/span&gt; &lt;span class="na"&gt;key=&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;article&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;{{$item.name}}&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&amp;gt;&lt;/span&gt;{{$item.email}}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/pick-for&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the data, filtering, loading, and behavior stay in TypeScript.&lt;/p&gt;

&lt;p&gt;That is the balance I am trying to keep:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Reactive&lt;/span&gt; &lt;span class="nx"&gt;users&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Reactive&lt;/span&gt; &lt;span class="nx"&gt;searchQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="nf"&gt;filteredUsers&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&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;searchQuery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&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="nx"&gt;query&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;users&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;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&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="o"&gt;=&amp;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="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&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;The template renders.&lt;/p&gt;

&lt;p&gt;The component decides.&lt;/p&gt;

&lt;p&gt;The service does the real work.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real project helped me test the idea
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;[Image here: Kronometa screenshot]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I also used Pick Components in a small race timing app called &lt;strong&gt;Kronometa&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That project made the idea feel more real.&lt;/p&gt;

&lt;p&gt;Kronometa is not just a set of pages. It moves through phases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;choose race mode&lt;/li&gt;
&lt;li&gt;register runners&lt;/li&gt;
&lt;li&gt;start the race&lt;/li&gt;
&lt;li&gt;record finishes&lt;/li&gt;
&lt;li&gt;review results&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That pushed me to think about routing as part of the application flow, not just URL matching.&lt;/p&gt;

&lt;p&gt;The UI should not let you jump anywhere if the race state does not allow it.&lt;/p&gt;

&lt;p&gt;That is where the structure helped: components for UI, services for rules, routing for flow, and templates mostly for rendering the current state.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;The biggest lesson for me was this:&lt;/p&gt;

&lt;p&gt;Good DX is not only about adding more features.&lt;/p&gt;

&lt;p&gt;Sometimes it is about deciding where things should not go.&lt;/p&gt;

&lt;p&gt;I don’t want templates to become a second TypeScript.&lt;/p&gt;

&lt;p&gt;I don’t want components to become a dumping ground for every kind of logic.&lt;/p&gt;

&lt;p&gt;I don’t want routing to be disconnected from the actual state of the app.&lt;/p&gt;

&lt;p&gt;Pick Components is my attempt to explore those boundaries in a way that still feels close to the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  This is not meant to replace everything
&lt;/h2&gt;

&lt;p&gt;I am not saying this is the right approach for every app.&lt;/p&gt;

&lt;p&gt;It is definitely not a “React killer”, a “Vue replacement”, or any of that nonsense.&lt;/p&gt;

&lt;p&gt;It is a small framework built around a set of trade-offs that make sense to me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;native Web Components&lt;/li&gt;
&lt;li&gt;constrained templates&lt;/li&gt;
&lt;li&gt;reactive state&lt;/li&gt;
&lt;li&gt;explicit lifecycle&lt;/li&gt;
&lt;li&gt;services outside the UI&lt;/li&gt;
&lt;li&gt;less hidden runtime behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Maybe that is useful to other people too.&lt;/p&gt;

&lt;p&gt;Maybe it is just a good learning project.&lt;/p&gt;

&lt;p&gt;Either way, building it has changed how I think about frontend architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;Playground: &lt;a href="https://janmbaco.github.io/PickComponents" rel="noopener noreferrer"&gt;https://janmbaco.github.io/PickComponents&lt;/a&gt;&lt;br&gt;
Repository: &lt;a href="https://github.com/janmbaco/PickComponents" rel="noopener noreferrer"&gt;https://github.com/janmbaco/PickComponents&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would be happy to hear what people think, especially around templates, TypeScript DX, and how much logic belongs in the view layer.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webcomponents</category>
      <category>frontend</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
