Notes: Svelte - Basic Markup
Second page containing notes about Svelte. As before, I’ll keep the structure the same. Quotes will be quoted via >
and explanation will follow. Notes are separated using ---
horizontal lines
Notes
A lowercase tag, like, denotes a regular HTML element. A capitalized tag, such as
or , indicates a component.
Basic, important api distinction. Sensible.
By default, attributes work exactly like their HTML counterparts.
<div class="foo"> <button disabled>can't touch this</button> </div>
As in HTML, values may be unquoted.
<input type=checkbox />
Attribute values can contain JavaScript expressions.
<a href="page/{p}">page {p}</a>
Or they can be JavaScript expressions.
<button disabled={!clickable}>...</button>
Nice to get examples here. I’ve used attributes in my Svelte projects, but still consider them to be a part of HTML/Svelte that I’m a little uncomfortable with. Particularly around the usage of JS expressions.
Boolean attributes are included on the element if their value is truthy and excluded if it’s falsy.
All other attributes are included unless their value is nullish (
null
orundefined
).
I find this pretty nice. Clean way to turn things on/off it looks like.
An expression might include characters that would cause syntax highlighting to fail in regular HTML, so quoting the value is permitted. The quotes do not affect how the value is parsed:
<button disabled="{number !== 42}">...</button>
Nice, and a good thing to note. Breaking syntax highlighting is a fairly reliable signal that something is wrong, so having this as a known thing with a known solution is helpful.
When the attribute name and value match (
name={name}
), they can be replaced with{name}
.
<button {disabled}>...</button> <!-- equivalent to <button disabled={disabled}>...</button> -->
This is a nice little bit of syntactic sugar. I had forgotten this was possible, but it’s nice and simple and I think worth using when available.
By convention, values passed to components are referred to as properties or props rather than attributes, which are a feature of the DOM.
Important to know/remember. Want to be idiomatic when writing/discussing/thinking about these things.
Spread attributes allow many attributes or properties to be passed to an element or component at once.
An element or component can have multiple spread attributes, interspersed with regular ones.
<Widget {...things} />
This is an interesting thing. This feels like it exists to allow for flexibly bundling/unbundling data/state sent between components. Pretty neat concept.
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mi>r</mi><mi>o</mi><mi>p</mi><mi>s</mi><mi mathvariant="normal">‘</mi><mi>r</mi><mi>e</mi><mi>f</mi><mi>e</mi><mi>r</mi><mi>e</mi><mi>n</mi><mi>c</mi><mi>e</mi><mi>s</mi><mi>a</mi><mi>l</mi><mi>l</mi><mi>p</mi><mi>r</mi><mi>o</mi><mi>p</mi><mi>s</mi><mi>t</mi><mi>h</mi><mi>a</mi><mi>t</mi><mi>a</mi><mi>r</mi><mi>e</mi><mi>p</mi><mi>a</mi><mi>s</mi><mi>s</mi><mi>e</mi><mi>d</mi><mi>t</mi><mi>o</mi><mi>a</mi><mi>c</mi><mi>o</mi><mi>m</mi><mi>p</mi><mi>o</mi><mi>n</mi><mi>e</mi><mi>n</mi><mi>t</mi><mo separator="true">,</mo><mi>i</mi><mi>n</mi><mi>c</mi><mi>l</mi><mi>u</mi><mi>d</mi><mi>i</mi><mi>n</mi><mi>g</mi><mi>o</mi><mi>n</mi><mi>e</mi><mi>s</mi><mi>t</mi><mi>h</mi><mi>a</mi><mi>t</mi><mi>a</mi><mi>r</mi><mi>e</mi><mi>n</mi><mi>o</mi><mi>t</mi><mi>d</mi><mi>e</mi><mi>c</mi><mi>l</mi><mi>a</mi><mi>r</mi><mi>e</mi><mi>d</mi><mi>w</mi><mi>i</mi><mi>t</mi><mi>h</mi><mi mathvariant="normal">‘</mi><mi>e</mi><mi>x</mi><mi>p</mi><mi>o</mi><mi>r</mi><mi>t</mi><mi mathvariant="normal">‘</mi><mi mathvariant="normal">.</mi><mi>U</mi><mi>s</mi><mi>i</mi><mi>n</mi><mi>g</mi><mi mathvariant="normal">‘</mi></mrow><annotation encoding="application/x-tex">props
references all props that are passed to a component, including ones that are not declared withexport
. Using</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">p</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">p</span><span class="mord mathnormal">s</span><span class="mord">‘</span><span class="mord mathnormal">re</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal">ere</span><span class="mord mathnormal">n</span><span class="mord mathnormal">ces</span><span class="mord mathnormal">a</span><span class="mord mathnormal">llp</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">p</span><span class="mord mathnormal">s</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ha</span><span class="mord mathnormal">t</span><span class="mord mathnormal">a</span><span class="mord mathnormal">re</span><span class="mord mathnormal">p</span><span class="mord mathnormal">a</span><span class="mord mathnormal">sse</span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span><span class="mord mathnormal">o</span><span class="mord mathnormal">a</span><span class="mord mathnormal">co</span><span class="mord mathnormal">m</span><span class="mord mathnormal">p</span><span class="mord mathnormal">o</span><span class="mord mathnormal">n</span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal">in</span><span class="mord mathnormal">c</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">u</span><span class="mord mathnormal">d</span><span class="mord mathnormal">in</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">o</span><span class="mord mathnormal">n</span><span class="mord mathnormal">es</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ha</span><span class="mord mathnormal">t</span><span class="mord mathnormal">a</span><span class="mord mathnormal">re</span><span class="mord mathnormal">n</span><span class="mord mathnormal">o</span><span class="mord mathnormal">t</span><span class="mord mathnormal">d</span><span class="mord mathnormal">ec</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">a</span><span class="mord mathnormal">re</span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">i</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord">‘</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.02778em;">or</span><span class="mord mathnormal">t</span><span class="mord">‘.</span><span class="mord mathnormal" style="margin-right:0.10903em;">U</span><span class="mord mathnormal">s</span><span class="mord mathnormal">in</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord">‘</span></span></span></span>props
will not perform as well as references to a specific prop because changes to any prop will cause Svelte to recheck all usages of$$props
. But it can be useful in some cases — for example, when you don’t know at compile time what props might be passed to a component.
<Widget {...$$props} />
I’ve seen this used in some component library/frameworks that I’ve used in my Svelte projects. They want to support arbitrary tailwind classes to be passed around/through the component hierarchy so that developers can customize the underlying components as much as possible. To achieve this, they pass
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>p</mi><mi>r</mi><mi>o</mi><mi>p</mi><mi>s</mi><mi mathvariant="normal">‘</mi><mo stretchy="false">(</mo><mi>o</mi><mi>r</mi><mi>a</mi><mi>n</mi><mi>e</mi><mi>q</mi><mi>u</mi><mi>i</mi><mi>v</mi><mi>a</mi><mi>l</mi><mi>e</mi><mi>n</mi><mi>t</mi><mi>a</mi><mi>p</mi><mi>p</mi><mi>r</mi><mi>o</mi><mi>a</mi><mi>c</mi><mi>h</mi><mi>b</mi><mi>u</mi><mi>t</mi><mi>w</mi><mi>i</mi><mi>t</mi><mi>h</mi><mi>s</mi><mi>o</mi><mi>m</mi><mi>e</mi><mi>t</mi><mi>h</mi><mi>i</mi><mi>n</mi><mi>g</mi><mi>l</mi><mi>i</mi><mi>k</mi><mi>e</mi></mrow><annotation encoding="application/x-tex">props
(or an equivalent approach but with something like classNames) to something like Tailwind’s
tw.merge()
function, which gets propagated to child components within the library. Useful tool, though not always super clean/safe.
<span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>r</mi><mi>e</mi><mi>s</mi><mi>t</mi><mi>P</mi><mi>r</mi><mi>o</mi><mi>p</mi><mi>s</mi><mi mathvariant="normal">‘</mi><mi>c</mi><mi>o</mi><mi>n</mi><mi>t</mi><mi>a</mi><mi>i</mi><mi>n</mi><mi>s</mi><mi>o</mi><mi>n</mi><mi>l</mi><mi>y</mi><mi>t</mi><mi>h</mi><mi>e</mi><mi>p</mi><mi>r</mi><mi>o</mi><mi>p</mi><mi>s</mi><mi>w</mi><mi>h</mi><mi>i</mi><mi>c</mi><mi>h</mi><mi>a</mi><mi>r</mi><msub><mi>e</mi><mi>n</mi></msub><mi>o</mi><msub><mi>t</mi><mi>d</mi></msub><mi>e</mi><mi>c</mi><mi>l</mi><mi>a</mi><mi>r</mi><mi>e</mi><mi>d</mi><mi>w</mi><mi>i</mi><mi>t</mi><mi>h</mi><mi mathvariant="normal">‘</mi><mi>e</mi><mi>x</mi><mi>p</mi><mi>o</mi><mi>r</mi><mi>t</mi><mi mathvariant="normal">‘</mi><mi mathvariant="normal">.</mi><mi>I</mi><mi>t</mi><mi>c</mi><mi>a</mi><mi>n</mi><mi>b</mi><mi>e</mi><mi>u</mi><mi>s</mi><mi>e</mi><mi>d</mi><mi>t</mi><mi>o</mi><mi>p</mi><mi>a</mi><mi>s</mi><mi>s</mi><mi>d</mi><mi>o</mi><mi>w</mi><mi>n</mi><mi>o</mi><mi>t</mi><mi>h</mi><mi>e</mi><mi>r</mi><mi>u</mi><mi>n</mi><mi>k</mi><mi>n</mi><mi>o</mi><mi>w</mi><mi>n</mi><mi>a</mi><mi>t</mi><mi>t</mi><mi>r</mi><mi>i</mi><mi>b</mi><mi>u</mi><mi>t</mi><mi>e</mi><mi>s</mi><mi>t</mi><mi>o</mi><mi>a</mi><mi>n</mi><mi>e</mi><mi>l</mi><mi>e</mi><mi>m</mi><mi>e</mi><mi>n</mi><mi>t</mi><mi>i</mi><mi>n</mi><mi>a</mi><mi>c</mi><mi>o</mi><mi>m</mi><mi>p</mi><mi>o</mi><mi>n</mi><mi>e</mi><mi>n</mi><mi>t</mi><mi mathvariant="normal">.</mi><mi>I</mi><mi>t</mi><mi>s</mi><mi>h</mi><mi>a</mi><mi>r</mi><mi>e</mi><mi>s</mi><mi>t</mi><mi>h</mi><mi>e</mi><mi>s</mi><mi>a</mi><mi>m</mi><mi>e</mi><mi>p</mi><mi>e</mi><mi>r</mi><mi>f</mi><mi>o</mi><mi>r</mi><mi>m</mi><mi>a</mi><mi>n</mi><mi>c</mi><mi>e</mi><mi>c</mi><mi>h</mi><mi>a</mi><mi>r</mi><mi>a</mi><mi>c</mi><mi>t</mi><mi>e</mi><mi>r</mi><mi>i</mi><mi>s</mi><mi>t</mi><mi>i</mi><mi>c</mi><mi>s</mi><mi>c</mi><mi>o</mi><mi>m</mi><mi>p</mi><mi>a</mi><mi>r</mi><mi>e</mi><mi>d</mi><mi>t</mi><mi>o</mi><mi>s</mi><mi>p</mi><mi>e</mi><mi>c</mi><mi>i</mi><mi>f</mi><mi>i</mi><mi>c</mi><mi>p</mi><mi>r</mi><mi>o</mi><mi>p</mi><mi>e</mi><mi>r</mi><mi>t</mi><mi>y</mi><mi>a</mi><mi>c</mi><mi>c</mi><mi>e</mi><mi>s</mi><mi>s</mi><mi>a</mi><mi>s</mi><mi mathvariant="normal">‘</mi></mrow><annotation encoding="application/x-tex">restProps
contains only the props which are not declared withexport
. It can be used to pass down other unknown attributes to an element in a component. It shares the same performance characteristics compared to specific property access as</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.8889em;vertical-align:-0.1944em;"></span><span class="mord mathnormal">res</span><span class="mord mathnormal" style="margin-right:0.13889em;">tP</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">p</span><span class="mord mathnormal">s</span><span class="mord">‘</span><span class="mord mathnormal">co</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ain</span><span class="mord mathnormal">so</span><span class="mord mathnormal">n</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord mathnormal">e</span><span class="mord mathnormal">p</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">p</span><span class="mord mathnormal">s</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">hi</span><span class="mord mathnormal">c</span><span class="mord mathnormal">ha</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord"><span class="mord mathnormal">e</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.1514em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">n</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">o</span><span class="mord"><span class="mord mathnormal">t</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.3361em;"><span style="top:-2.55em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathnormal mtight">d</span></span></span></span><span class="vlist-s"></span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"><span></span></span></span></span></span></span><span class="mord mathnormal">ec</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">a</span><span class="mord mathnormal">re</span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">i</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord">‘</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.02778em;">or</span><span class="mord mathnormal">t</span><span class="mord">‘.</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">t</span><span class="mord mathnormal">c</span><span class="mord mathnormal">anb</span><span class="mord mathnormal">e</span><span class="mord mathnormal">u</span><span class="mord mathnormal">se</span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span><span class="mord mathnormal">o</span><span class="mord mathnormal">p</span><span class="mord mathnormal">a</span><span class="mord mathnormal">ss</span><span class="mord mathnormal">d</span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">n</span><span class="mord mathnormal">o</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal">u</span><span class="mord mathnormal">nkn</span><span class="mord mathnormal">o</span><span class="mord mathnormal" style="margin-right:0.02691em;">w</span><span class="mord mathnormal">na</span><span class="mord mathnormal">tt</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">ib</span><span class="mord mathnormal">u</span><span class="mord mathnormal">t</span><span class="mord mathnormal">es</span><span class="mord mathnormal">t</span><span class="mord mathnormal">o</span><span class="mord mathnormal">an</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">e</span><span class="mord mathnormal">m</span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal">ina</span><span class="mord mathnormal">co</span><span class="mord mathnormal">m</span><span class="mord mathnormal">p</span><span class="mord mathnormal">o</span><span class="mord mathnormal">n</span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord">.</span><span class="mord mathnormal" style="margin-right:0.07847em;">I</span><span class="mord mathnormal">t</span><span class="mord mathnormal">s</span><span class="mord mathnormal">ha</span><span class="mord mathnormal">res</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord mathnormal">es</span><span class="mord mathnormal">am</span><span class="mord mathnormal">e</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal" style="margin-right:0.02778em;">or</span><span class="mord mathnormal">man</span><span class="mord mathnormal">cec</span><span class="mord mathnormal">ha</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">a</span><span class="mord mathnormal">c</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal">i</span><span class="mord mathnormal">s</span><span class="mord mathnormal">t</span><span class="mord mathnormal">i</span><span class="mord mathnormal">csco</span><span class="mord mathnormal">m</span><span class="mord mathnormal">p</span><span class="mord mathnormal">a</span><span class="mord mathnormal">re</span><span class="mord mathnormal">d</span><span class="mord mathnormal">t</span><span class="mord mathnormal">os</span><span class="mord mathnormal">p</span><span class="mord mathnormal">ec</span><span class="mord mathnormal">i</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal">i</span><span class="mord mathnormal">c</span><span class="mord mathnormal">p</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">p</span><span class="mord mathnormal" style="margin-right:0.02778em;">er</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.03588em;">y</span><span class="mord mathnormal">a</span><span class="mord mathnormal">ccess</span><span class="mord mathnormal">a</span><span class="mord mathnormal">s</span><span class="mord">‘</span></span></span></span>props
.
<input {...$$restProps} />
Oh, look, right on cue. This “passing unknown attributes to an element in a component”is exactly what I was talking about.
The
value
attribute of aninput
element or its childrenoption
elements must not be set with spread attributes when usingbind:group
orbind:checked
. Svelte needs to be able to see the element’svalue
directly in the markup in these cases so that it can link it to the bound variable.
Good thing to note. I am at low risk of hitting this gotcha bc I am not that confident using spread attributes, and also kind of averse to them as they obscure important information that feels like it should probably, most of the time, be explicit.
Sometimes, the attribute order matters as Svelte sets attributes sequentially in JavaScript. For example,
<input type="range" min="0" max="1" value={0.5} step="0.1"/>
, Svelte will attempt to set the value to1
(rounding up from 0.5 as the step by default is 1), and then set the step to0.1
. To fix this, change it to<input type="range" min="0" max="1" step="0.1" value={0.5}/>
.
Oh, wow. Yeah, these “order sometimes matters” points are real gotchas that would be really hard/annoying to debug if you had no idea what was going on. Good to see them in the docs, but would be better if this kind of thing didn’t exist.
Another example is
<img src="..." loading="lazy" />
. Svelte will set the imgsrc
before making the img elementloading="lazy"
, which is probably too late. Change this to<img loading="lazy" src="...">
to make the image lazily loaded.
Oh wow, see this one I feel like I would hit straight away, even as some just getting started with web dev. Definitely want to keep this in mind.
A JavaScript expression can be included as text by surrounding it with curly braces.
{expression}
Curly braces can be included in a Svelte template by using their HTML entity strings:
{
,{
, or{
for{
and}
,}
, or}
for}
.
Nice, this is an API I have used many times. Hadn’t really thought to much about it other than “this is how you put a variable into markup” or similar type thing. Good to read it here.
Also, I feel like this highlights a nice API design on Svelte’s part. Choosing to default the unescaped {
and }
to be used in JS expressions and having the escaped {
be an HTML literal is quite sound. JS expressions would pop up maybe 100x more often for most devs than actually rendering a {
in their markup. Good call to make the default be the easy to use option.
You can use HTML comments inside components.
<!-- this is a comment! --><h1>Hello world</h1>
Nice to know. I don’t do much commenting in the HTML parts of components I write, but good to know you can/how to do it.