Components

Components are the primary reason of using any modern JavaScript framework because it lets you organize code and have your concerns in one place.

If you ever used classes you can think of components as new instances of a class that can be used as a blueprint to have its own independent state.

It’s easy to get carried away with components so in general don’t look for what to turn into a component but write everything inside a single file until it becomes hard to manage and you start noticing repeating parts.

You might have an <Artist /> component:

  • <Artist /> components gets passed the artistName property
  • <Album /> component has albumTitle and albumTracks property passed to <AlbumTrack />
  • <AlbumTrack /> component has track and length properties but also a playing state

The filename can be whatever but a capitalised tag such as <Artist /> indicates to Svelte that something is a component. You import another Svelte component using the import Component as './Component' syntax.

Pretend that artists is some data we fetched as a JSON response from the Spotify API.

App.svelteCopy

<script>
	import Artist from './Artist.svelte'
	import Album from './Album.svelte'

	let artists = [
		{
			name: 'Fleetwood Mac',
			albums: [
				{
					name: 'Tango in the Night',
					year: 1987,
					tracks: [
						{ title: 'Big Love', length: '3:37' },
						{ title: 'Seven Wonders', length: '3:38' },
						{ title: 'Everywhere', length: '3:48' },
						{ title: 'Caroline', length: '3:50' },
						{ title: 'Tango in the Night', length: '3:56' },
						{ title: 'Mystified', length: '3:08' },
					],
				},
			],
		},
	]
</script>

{#each artists as artist}
  <Artist artistName={artist.name} />
  {#each artist.albums as album}
		<Album
			albumTitle={album.name}
			albumTracks={album.tracks}
		/>
  {/each}
{/each}

The <Artist /> component takes an artistName prop. To define something as a prop that’s passed in to your component you use the export let prop syntax. You can define multiple props on the same line such as export let prop1, prop2.

Artist.svelteCopy

<script>
	export let artistName
</script>

<h1>{artistName}</h1>

The <Album /> component imports <AlbumTrack /> and loops over the tracks. The {...track} syntax is just spreading the track props which is equivalent to title={title} length={length}. If your props share the same name as the value you can do {title} {length}.

Album.svelteCopy

<script>
	import AlbumTrack from './AlbumTrack.svelte'

	export let albumTitle
	export let albumTracks

	let playing

	function setPlaying(track) {
		playing = track
	}
</script>

<h2>{albumTitle}</h2>

<ul>
  {#each albumTracks as track}
    <AlbumTrack {setPlaying} {playing} {...track} />
  {/each}
</ul>

We’re passing setPlaying to the child component so we can set the currently playing song and check if currentlyPlaying is equal to the current track.

The <AlbumTrack /> component applies a .playing style using the class: directive based on what song is playing which is shorter than using a ternary inside an expression class={playing === title ? 'playing' : ''}.

AlbumTrack.svelteCopy

<script>
	export let setPlaying
	export let playing
  export let title
	export let length
</script>

<li class:playing={playing === title}>
	<button on:click={() => setPlaying(title)}>▶️</button>
  <span>{title}</span>
	<span>🕒️ {length}</span>
</li>

<style>
	.playing {
		color: teal;
	}
</style>

We can also use a reactive statement $: playing = playing === title for playing and since it matches the class name we want to apply we can simplify the code and write class:playing.

AlbumTrack.svelteCopy

<script>
	export let setPlaying
	export let playing
  export let title
	export let length

	$: playing = playing === title
</script>

<li class:playing>
	<button on:click={() => setPlaying(title)}>▶️</button>
  <span>{title}</span>
	<span>🕒️ {length}</span>
</li>

<style>
	.playing {
		color: teal;
	}
</style>

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *