<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[GiFTED!’s Blog | Gift Opia]]></title><description><![CDATA[GiFTED!’s Blog | Gift Opia]]></description><link>https://blog.iamgifted.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 18:10:30 GMT</lastBuildDate><atom:link href="https://blog.iamgifted.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Transfer tokens to "ANY" Solana wallet.]]></title><description><![CDATA[Introduction
Unlike other blockchains, Solana token model uses Associated Token Accounts (ATAs) to store information about a token, its balance, and its owner. This architecture enables Solana's high performance but requires a deeper understanding of...]]></description><link>https://blog.iamgifted.dev/transfer-any-token-on-solana</link><guid isPermaLink="true">https://blog.iamgifted.dev/transfer-any-token-on-solana</guid><category><![CDATA[Solana]]></category><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><dc:creator><![CDATA[Gift Opia]]></dc:creator><pubDate>Mon, 24 Mar 2025 20:49:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/URTdM8lVSDg/upload/be55f844df9d0ddb41a7764b742c048c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Unlike other blockchains, Solana token model uses <strong>Associated Token Accounts (ATAs)</strong> to store information about a token, its balance, and its owner. This architecture enables Solana's high performance but requires a deeper understanding of the underlying mechanisms. This practical guide will walk through the code for transferring any token on Solana.</p>
<h2 id="heading-pre-requisite">Pre-requisite</h2>
<p>Before proceeding with this tutorial, make sure you have the following prerequisites:</p>
<ol>
<li><p>Basic knowledge of TypeScript.</p>
</li>
<li><p><a target="_blank" href="https://nodejs.org/en/download">Node.j</a><a target="_blank" href="https://nodejs.org/en/download">s</a> installed on your machine.</p>
</li>
<li><p>npm or yarn package manager installed. (I prefer yarn, so that’s what I’ll be using)</p>
</li>
</ol>
<h2 id="heading-understanding-solana-token-architecture">Understanding Solana Token Architecture</h2>
<p>Before diving into the code, let's establish a clear mental model of how tokens work on Solana:</p>
<ol>
<li><p><strong>Token Mint</strong>: The definition of a token (like USDC, BONK, or RAY)</p>
</li>
<li><p><strong>Token Account</strong>: Where token balances are actually stored</p>
</li>
<li><p><strong>Wallet</strong>: Controls access to token accounts but doesn't directly hold tokens</p>
</li>
</ol>
<p>On Solana, your wallet doesn't directly hold tokens. Instead, for each token type you own, you have a separate Token Account that your wallet controls.</p>
<h3 id="heading-associated-token-accounts-atas-the-practical-solution">Associated Token Accounts (ATAs): The Practical Solution</h3>
<p>An Associated Token Account (ATA) is a special type of token account with an address that's deterministically derived from:</p>
<ul>
<li><p>Your wallet address</p>
</li>
<li><p>The token's mint address</p>
</li>
</ul>
<p>This creates a predictable mapping that anyone can calculate.</p>
<p>In TypeScript, here's how this mapping works:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { getAssociatedTokenAddressSync, ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID } <span class="hljs-keyword">from</span> <span class="hljs-string">'@solana/spl-token'</span>;

<span class="hljs-comment">// To find the ATA address for any wallet and token mint</span>
<span class="hljs-keyword">const</span> associatedTokenAddress = getAssociatedTokenAddressSync(
  mintAddress,  <span class="hljs-comment">// The token's mint address</span>
  walletAddress <span class="hljs-comment">// The wallet address</span>
);

<span class="hljs-comment">// Under the hood, this function calculates a PDA (Program Derived Address)</span>
<span class="hljs-comment">// using the wallet address, TOKEN_PROGRAM_ID, and mint address as seeds</span>
</code></pre>
<p>Visually, the relationship looks like this:</p>
<pre><code class="lang-bash">Wallet Address    Token Mint Address
       ↓                 ↓
       └────────┬────────┘
                ↓
    Deterministic Calculation
                ↓
    Associated Token Account (ATA)
</code></pre>
<h2 id="heading-code-walkthrough-transferring-any-token-on-solana">Code Walkthrough: Transferring Any Token on Solana</h2>
<p>Now let's walk through our function for transferring tokens on Solana, explaining each part:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">sendSplToken</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> wallet = <span class="hljs-comment">// Create wallet ;</span>
  <span class="hljs-keyword">const</span> mint = <span class="hljs-keyword">new</span> PublicKey(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> amount = <span class="hljs-number">1</span>;
  <span class="hljs-keyword">const</span> toAddress = <span class="hljs-keyword">new</span> PublicKey(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> connection = <span class="hljs-keyword">new</span> Connection(SOLANA_RPC_ENDPOINT);
  <span class="hljs-keyword">const</span> signer = <span class="hljs-keyword">await</span> wallet.getSigner();

  <span class="hljs-keyword">const</span> Ixs: TransactionInstruction[] = [];
</code></pre>
<p>We start by setting up our wallet, mint (the token we want to transfer), amount, recipient address, and a connection to Solana. The <code>Ixs</code> array will hold all our transaction instructions.</p>
<h3 id="heading-step-1-handle-native-sol-vs-spl-token-transfers">Step 1: Handle Native SOL vs SPL Token Transfers</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (<span class="hljs-keyword">new</span> PublicKey(mint).equals(SystemProgram.programId)) {
    Ixs.push(
      SystemProgram.transfer({
        fromPubkey: <span class="hljs-keyword">new</span> PublicKey(wallet.address),
        toPubkey: <span class="hljs-keyword">new</span> PublicKey(toAddress),
        lamports: BigInt(<span class="hljs-built_in">Math</span>.round(amount * LAMPORTS_PER_SOL)),
      }),
    );
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// SPL token transfer logic...</span>
  }
</code></pre>
<p><strong>Why Native SOL Doesn't Require ATAs:</strong></p>
<p>Solana makes a distinction between its native token (SOL) and all other tokens (SPL tokens):</p>
<ul>
<li><p><strong>Native SOL</strong> is built into Solana's core protocol and is stored directly in wallet accounts</p>
</li>
<li><p><strong>SPL tokens</strong> are created using the SPL Token program and require separate token accounts</p>
</li>
</ul>
<p>When transferring native SOL, we simply use <code>SystemProgram.transfer</code> which moves SOL directly between wallet accounts. No ATAs are involved because SOL doesn't use the token program - it's a fundamental part of Solana itself.</p>
<h3 id="heading-step-2-spl-token-transfers-handle-source-and-destination-atas">Step 2: SPL Token Transfers - Handle Source and Destination ATAs</h3>
<p>Now let's analyse the SPL token transfer logic:</p>
<pre><code class="lang-typescript">} <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">const</span> sourceAccount = getAssociatedTokenAddressSync(
      <span class="hljs-keyword">new</span> PublicKey(mint),
      <span class="hljs-keyword">new</span> PublicKey(wallet.address),
    );

    <span class="hljs-keyword">const</span> destinationTokenAccountInfo = <span class="hljs-keyword">await</span> connection.getAccountInfo(
      <span class="hljs-keyword">new</span> PublicKey(toAddress),
      <span class="hljs-string">"finalized"</span>,
    );

    <span class="hljs-keyword">let</span> destinationTokenAccount: PublicKey | <span class="hljs-literal">null</span>;

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> tokenAccount = <span class="hljs-keyword">await</span> getOrCreateAssociatedTokenAccount(
        connection,
        signer,
        <span class="hljs-keyword">new</span> PublicKey(mint),
        <span class="hljs-keyword">new</span> PublicKey(toAddress),
      );

      destinationTokenAccount = tokenAccount.address;
    } <span class="hljs-keyword">catch</span> (error) {
      destinationTokenAccount = <span class="hljs-literal">null</span>;
    }
</code></pre>
<ol>
<li><p>We first determine the sender's ATA using <code>getAssociatedTokenAddressSync</code></p>
</li>
<li><p>We check if the recipient already has an account for this token</p>
</li>
<li><p>We try to get or create the recipient's ATA</p>
</li>
</ol>
<p><strong>Important point:</strong> The <code>getOrCreateAssociatedTokenAccount</code> function will throw an error if you try to create an ATA that already exists. This is why we catch the error and set <code>destinationTokenAccount</code> to null if it fails. The next section of code handles this scenario.</p>
<h3 id="heading-step-3-create-ata-for-recipient-if-needed">Step 3: Create ATA for Recipient If Needed</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">if</span> (!destinationTokenAccountInfo || !destinationTokenAccount) {
      destinationTokenAccount = getAssociatedTokenAddressSync(
        <span class="hljs-keyword">new</span> PublicKey(mint),
        <span class="hljs-keyword">new</span> PublicKey(toAddress),
      );

      Ixs.push(
        createAssociatedTokenAccountInstruction(
          <span class="hljs-keyword">new</span> PublicKey(wallet.address),
          destinationTokenAccount,
          <span class="hljs-keyword">new</span> PublicKey(toAddress),
          <span class="hljs-keyword">new</span> PublicKey(mint),
        ),
      );
    }
</code></pre>
<p>This block is critical - if the recipient doesn't have an ATA for this token:</p>
<ol>
<li><p>We calculate what the recipient's ATA address should be</p>
</li>
<li><p>We create an instruction to create this account</p>
</li>
</ol>
<p><strong>Why ATA Creation Throws Errors:</strong></p>
<p>If you attempt to create an ATA that already exists, the transaction will fail with an error like "account already exists". This is a protection mechanism that prevents duplicate accounts and ensures each wallet has exactly one ATA per token mint.</p>
<p>To avoid this error in production code, we first check if the ATA exists before attempting to create it. Our function handles this by:</p>
<ol>
<li><p>First trying to use <code>getOrCreateAssociatedTokenAccount</code></p>
</li>
<li><p>If that fails, falling back to a manual creation approach</p>
</li>
<li><p>Only pushing the create instruction if we confirm the ATA doesn't exist</p>
</li>
</ol>
<h3 id="heading-step-4-create-transfer-instruction">Step 4: Create Transfer Instruction</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> numberDecimals = <span class="hljs-keyword">await</span> getNumberDecimals(mint, connection);

Ixs.push(
    createTransferInstruction(
      sourceAccount,
      destinationTokenAccount,
      <span class="hljs-keyword">new</span> PublicKey(wallet.address),
      <span class="hljs-built_in">Math</span>.round(amount * <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">10</span>, numberDecimals)),
    ),
  );
}
</code></pre>
<p>Here we:</p>
<ol>
<li><p>Get the decimal precision for the token</p>
</li>
<li><p>Create the actual transfer instruction and add it to our array</p>
</li>
</ol>
<p><strong>Why Instruction Order Matters:</strong></p>
<p>In Solana, instructions within a transaction are executed in the exact order they're added to the transaction. This is crucial when creating an ATA and then transferring tokens to it in the same transaction:</p>
<ol>
<li><p><strong>First Instruction</strong>: Create the destination ATA</p>
</li>
<li><p><strong>Second Instruction</strong>: Transfer tokens to the newly created ATA</p>
</li>
</ol>
<p>If we reversed this order, the transfer would fail because it would try to send tokens to an account that doesn't exist yet. This is why in our code, the ATA creation instruction comes before the transfer instruction.</p>
<h3 id="heading-step-5-build-and-send-the-transaction">Step 5: Build and Send the Transaction</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> latestBlockHash = <span class="hljs-keyword">await</span> connection.getLatestBlockhash();

  <span class="hljs-keyword">const</span> messageV0 = <span class="hljs-keyword">new</span> TransactionMessage({
    payerKey: <span class="hljs-keyword">new</span> PublicKey(wallet.address),
    recentBlockhash: latestBlockHash.blockhash,
    instructions: Ixs,
  }).compileToV0Message();

  <span class="hljs-keyword">const</span> transaction = <span class="hljs-keyword">new</span> VersionedTransaction(messageV0);

  <span class="hljs-keyword">const</span> signedTx = <span class="hljs-keyword">await</span> signer.signTransaction(transaction);

  <span class="hljs-keyword">const</span> signature = <span class="hljs-keyword">await</span> connection.sendRawTransaction(signedTx.serialize(), {
    skipPreflight: <span class="hljs-literal">true</span>,
  });

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"signature"</span>, signature);

  <span class="hljs-keyword">return</span> signature;
}
</code></pre>
<p>Finally, we:</p>
<ol>
<li><p>Get a recent blockhash (required for all Solana transactions)</p>
</li>
<li><p>Build a transaction message containing our instructions</p>
</li>
<li><p>Create a versioned transaction (the modern transaction format on Solana)</p>
</li>
<li><p>Sign the transaction with our wallet</p>
</li>
<li><p>Send the transaction to the network and return the signature</p>
</li>
</ol>
<h2 id="heading-practical-considerations">Practical Considerations</h2>
<h3 id="heading-token-decimals-matter">Token Decimals Matter</h3>
<p>Different tokens use different decimal places:</p>
<ul>
<li><p>USDC: 6 decimals (1 USDC = 1,000,000 units)</p>
</li>
<li><p>Most SPL tokens: 9 decimals (1 token = 1,000,000,000 units)</p>
</li>
</ul>
<p>Our code handles this by fetching the token's decimals and adjusting the amount:</p>
<pre><code class="lang-typescript">amount * <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">10</span>, numberDecimals)
</code></pre>
<h3 id="heading-rent-and-account-creation">Rent and Account Creation</h3>
<p>Creating an ATA requires SOL to pay for account "rent" (storage space on the blockchain). In our function, the sender pays this cost when creating the recipient's ATA.</p>
<p>As of early 2025, creating an ATA costs approximately 0.00203928 SOL. Keep this in mind when building applications that might create many ATAs.</p>
<h3 id="heading-error-handling-in-practice">Error Handling in Practice</h3>
<p>In production code, you should add more robust error handling:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">try</span> {
  <span class="hljs-keyword">const</span> signature = <span class="hljs-keyword">await</span> sendSplToken();
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Transfer successful:"</span>, signature);
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-comment">// Common errors to handle:</span>
  <span class="hljs-comment">// - Insufficient funds (either token balance or SOL for fees)</span>
  <span class="hljs-comment">// - Invalid addresses</span>
  <span class="hljs-comment">// - Account already exists errors</span>
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Transfer failed:"</span>, error);
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This practical guide walked through transferring any token on Solana, highlighting key differences between native SOL and SPL tokens, explaining ATA creation requirements, and demonstrating how to structure your transaction instructions in the correct order.</p>
<p>By understanding these mechanisms, you can now build reliable token transfer functionality into your Solana applications. The token architecture may seem complex at first, but it enables Solana's speed and efficiency while providing a predictable system for managing token ownership.</p>
<h2 id="heading-finished-work">Finished Work : )</h2>
<p>You can find the complete working solution in this GitHub gist here:<br /><a target="_blank" href="https://gist.github.com/Gift-Stack/ca900bc338d636ff5e687d76f59a77cb#file-send-spl-token-ts">https://gist.github.com/Gift-Stack/ca900bc338d636ff5e687d76f59a77cb#file-send-spl-token-ts</a></p>
<h2 id="heading-next-steps">Next Steps</h2>
<p>To extend this functionality, consider:</p>
<ul>
<li><p>Adding better error handling</p>
</li>
<li><p>Supporting batch transfers</p>
</li>
<li><p>Implementing approval flows (delegate authority)</p>
</li>
<li><p>Supporting Token2022 tokens with their extended features</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to build a reusable component library using Svelte and Storybook]]></title><description><![CDATA[Libraries are a very important consideration in building large applications because they prevent you from writing a large number of codes and logic that have already been prebuilt.
This article aims to give readers an introduction to building reusabl...]]></description><link>https://blog.iamgifted.dev/how-to-build-a-reusable-component-library-using-svelte-and-storybook</link><guid isPermaLink="true">https://blog.iamgifted.dev/how-to-build-a-reusable-component-library-using-svelte-and-storybook</guid><category><![CDATA[Svelte]]></category><category><![CDATA[Javascript library]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Gift Opia]]></dc:creator><pubDate>Mon, 01 Nov 2021 02:00:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1635733344721/rthHUaGoR.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Libraries are a very important consideration in building large applications because they prevent you from writing a large number of codes and logic that have already been prebuilt.</p>
<p>This article aims to give readers an introduction to building reusable components using Storybook (To visually test your component) and Svelte (To build the actual component).</p>
<p>We will be creating a simple button library that allows users to create styled buttons with just one line of code.</p>
<h2 id="what-is-storybook">What is Storybook?</h2>
<p>Storybook is a tool for UI development that makes development faster and easier by isolating components. This also has an environment for us to view our components. You can check <a target="_blank" href="https://storybook.js.org/">Storybook documentation</a> for more.</p>
<h2 id="quick-introduction-to-svelte">Quick introduction to Svelte</h2>
<p>According to its <a target="_blank" href="https://svelte.dev/blog/svelte-3-rethinking-reactivity#What_is_Svelte">blog</a>, Svelte is a component framework — like React or Vue — but with an important difference; unlike other frameworks,  Svelte runs at build time, converting your components into highly efficient imperative code that surgically updates the DOM. As a result, you're able to write ambitious applications with excellent performance characteristics.</p>
<p>So what the above explanations simply mean is that Svelte doesn’t work like other frameworks that use <code>“virtual doms”</code> to update the UI rather, it compiles component files into beautifully optimized JavaScript behind the scenes. So yes, Svelte is just a compiler rather than a framework!</p>
<h2 id="assumptions">Assumptions</h2>
<p>This article assumes that you are already familiar with HTML, CSS, JavaScript, and Svelte basics. No worries, no prior experience of Storybook is necessary.</p>
<h2 id="getting-started-with-storybook">Getting started with Storybook</h2>
<p>We will be using the Storybook + Svelte boilerplate to set up our application and the first thing we need to do is to use degit to set up our boilerplates.
Run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Clone template</span>
npx degit chromaui/intro-storybook-svelte-template svelte-button

<span class="hljs-built_in">cd</span> svelte-button

<span class="hljs-comment"># Install dependencies</span>
npm install
</code></pre>
<p>If all has gone well so far, run the following command:</p>
<pre><code><span class="hljs-built_in">npm</span> run storybook
</code></pre><p>The Storybook will be opened on localhost:6006 and you will see a screen just like this;</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jhppkcuno831o60ca3cs.PNG" alt="Image of Storybook testing environment" /></p>
<p>Now you can take a sip of coffee as you are one step closer to building your Svelte library.</p>
<h3 id="file-structure">File structure</h3>
<p>There are important files and folders to pay close attention to in the template you generated earlier. They include;</p>
<ul>
<li><code>rollup.config.js</code></li>
<li><code>src/main.js</code></li>
<li><code>src/stories</code></li>
</ul>
<blockquote>
<p><b>rollup.config.js<b></b></b></p>
</blockquote>
<p>The rollup.config.js file contains metadata as to how the svelte compiler should read and compile the entire code. Therefore, we have to carefully understand how the compiler is set so we know how to structure our codebase properly.</p>
<p>So, the input portion of it like that in the code below tells the compiler where to look when compiling. Therefore, our main components will be exported.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">input</span>: <span class="hljs-string">"src/main.js"</span>,
  <span class="hljs-attr">output</span>: {
    <span class="hljs-attr">sourcemap</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">format</span>: <span class="hljs-string">"iife"</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">"app"</span>,
    <span class="hljs-attr">file</span>: <span class="hljs-string">"public/build/bundle.js"</span>,
  }
}
</code></pre>
<blockquote>
<p><b>src/main.js<b></b></b></p>
</blockquote>
<p>By default we would get something like this;</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App.svelte'</span>;

<span class="hljs-keyword">const</span> app = <span class="hljs-keyword">new</span> App({
  <span class="hljs-attr">target</span>: <span class="hljs-built_in">document</span>.body,
  <span class="hljs-attr">props</span>: {
    <span class="hljs-attr">name</span>: <span class="hljs-string">'world'</span>
  }
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> app;
</code></pre>
<p>This is a basic way to pass props between components in Svelte but we won't be needing this. But at the end of the project, we’d have something like this;</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/buttons/Button.svelte'</span>
</code></pre>
<blockquote>
<p><b>src/stories</b></p>
</blockquote>
<p>The stories folder would contain <code>.stories.js files</code> for different components. In these files, we would write our storybook code to help us test our code visually.</p>
<p>You can go ahead to clear the files in this folder for we would be creating ours.</p>
<h3 id="creating-the-button-component">Creating the button component</h3>
<p>Firstly, I'd like us to delete every file and folder in the <code>src</code> folder except the <code>main.js</code> file as that is our root file.</p>
<p>In our <code>src</code> folder, we'll create a "components" folder where we'll store, individually, every component our library should have. But in this article, we'd have just one component; The buttons component.
In our newly created components folder, we'll create a "buttons" folder where we'll store every code related to the button component. In our buttons folder, we'll create a <code>Button.svelte</code> file.</p>
<pre><code class="lang-bash">src                     
└─ components            
    └─ buttons           
      └─ Button.svelte
</code></pre>
<p>The next thing we want to do is create our button element and give it some dynamic class names so that it has different styles as requested.</p>
<pre><code class="lang-javascript">&lt;script&gt;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> value
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> size = <span class="hljs-string">'md'</span>;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> type = <span class="hljs-string">'default'</span>;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> shape = <span class="hljs-string">'default'</span>;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> outlined = <span class="hljs-literal">false</span>;

  <span class="hljs-keyword">const</span> outlinedClass = outlined
    ? <span class="hljs-string">`gl-btn-outlined gl-btn-outlined-<span class="hljs-subst">${type}</span>`</span>
    : <span class="hljs-string">`gl-btn-<span class="hljs-subst">${type}</span>`</span>;
&lt;/script&gt;

  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"gl-btn gl-btn-{shape} gl-btn-{size} {outlinedClass}"</span>&gt;</span>
    {value}
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre>
<p>Now that we  have specified what our button component should be, let's write our styles to fit every use cases that we want.</p>
<pre><code class="lang-javascript">&lt;script&gt;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> value
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> size = <span class="hljs-string">'md'</span>;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> type = <span class="hljs-string">'default'</span>;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> shape = <span class="hljs-string">'default'</span>;
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">let</span> outlined = <span class="hljs-literal">false</span>;

  <span class="hljs-keyword">const</span> outlinedClass = outlined
    ? <span class="hljs-string">`gl-btn-outlined gl-btn-outlined-<span class="hljs-subst">${type}</span>`</span>
    : <span class="hljs-string">`gl-btn-<span class="hljs-subst">${type}</span>`</span>;
&lt;/script&gt;

  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"gl-btn gl-btn-{shape} gl-btn-{size} {outlinedClass}"</span>&gt;</span>
    {value}
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>


<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
  <span class="hljs-selector-class">.gl-btn</span> {
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
    <span class="hljs-attribute">cursor</span>: pointer;
  }
  <span class="hljs-selector-class">.gl-btn-outlined</span> {
    <span class="hljs-attribute">background</span>: transparent <span class="hljs-meta">!important</span>;
  }
  <span class="hljs-selector-class">.gl-btn-pill</span> {
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50px</span>;
  }
  <span class="hljs-selector-class">.gl-btn-sm</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">20px</span>;
  }
  <span class="hljs-selector-class">.gl-btn-md</span> {
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">13px</span> <span class="hljs-number">32px</span>;
  }
  <span class="hljs-selector-class">.gl-btn-lg</span> {
    <span class="hljs-attribute">font-size</span>: larger;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span> <span class="hljs-number">50px</span>;
  }
  <span class="hljs-selector-class">.gl-btn-primary</span>{
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#0275d8</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#0275d8</span>
  }
  <span class="hljs-selector-class">.gl-btn-outlined-primary</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#0275d8</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#0275d8</span> solid
  }
  <span class="hljs-selector-class">.gl-btn-success</span>{
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#5cb85c</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#5cb85c</span>;
  }
  <span class="hljs-selector-class">.gl-btn-outlined-success</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#5cb85c</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#5cb85c</span> solid
  }
  <span class="hljs-selector-class">.gl-btn-secondary</span>{
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">border</span>: grey;
    <span class="hljs-attribute">background</span>: grey;
  }

  <span class="hljs-selector-class">.gl-btn-outlined-secondary</span>{
    <span class="hljs-attribute">color</span>: grey;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> grey solid;
  }

  <span class="hljs-selector-class">.gl-btn-danger</span>{
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#d9534f</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#d9534f</span>;
  }
  <span class="hljs-selector-class">.gl-btn-outlined-danger</span>{
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#d9534f</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#d9534f</span> solid;
  }
  <span class="hljs-selector-class">.gl-btn-warning</span>{
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#f0ad4e</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#f0ad4e</span>
  }
  <span class="hljs-selector-class">.gl-btn-outlined-warning</span>{
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#f0ad4e</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#f0ad4e</span> solid
  }
  <span class="hljs-selector-class">.gl-btn-info</span>{ <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#5bc0de</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#5bc0de</span>;
  }
  <span class="hljs-selector-class">.gl-btn-outlined-info</span>{
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#5bc0de</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#5bc0de</span> solid;
  }
  <span class="hljs-selector-class">.gl-btn-light</span>{
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#f7f7f7</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#f7f7f7</span>;
  }
  <span class="hljs-selector-class">.gl-btn-dark</span>{
    <span class="hljs-attribute">color</span>: white;
    <span class="hljs-attribute">background</span>: <span class="hljs-number">#292b2c</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">#292b2c</span>;
  }
  <span class="hljs-selector-class">.gl-btn-outlined-dark</span>{
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#292b2c</span>;
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> <span class="hljs-number">#292b2c</span> solid;
  }
  <span class="hljs-selector-class">.gl-btn-link</span>{
    <span class="hljs-attribute">background</span>: transparent;
    <span class="hljs-attribute">border</span>: transparent;
    <span class="hljs-attribute">color</span>: <span class="hljs-number">#0275d8</span>;
  }
  <span class="hljs-selector-class">.gl-btn-link</span><span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">text-decoration</span>: underline;
  }

</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>Note, you can store your css anywhere, I just chose to store mine in the same file.</p>
<p>Finally, we'll export our Button component in the <code>main.js</code> file for reusability by replacing the existing code with this;</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> { <span class="hljs-keyword">default</span> <span class="hljs-keyword">as</span> Button } <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/buttons/Button.svelte'</span>
</code></pre>
<h3 id="creating-the-storybook-visual-test">Creating the storybook visual test</h3>
<p>Storybook helps document components for reuse and automatically visually test your components to prevent bugs.</p>
<p>Firstly, we will create a <code>stories</code> folder in our <code>src</code> folder, and in our <code>stories</code> folder, we'll create a <code>button.stories.js</code> file.</p>
<pre><code class="lang-bash">src                      
└─ stories               
  └─ button.stories.js
</code></pre>
<h4 id="step1-importing-our-button-component">Step1: Importing our Button component</h4>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/buttons/Button.svelte'</span>
</code></pre>
<h4 id="step2-exporting-the-component-to-storybook">Step2: Exporting the component to storybook</h4>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">title</span>: <span class="hljs-string">'Button'</span>,
    <span class="hljs-attr">component</span>: Button
}
</code></pre>
<p>This would be displayed in Storybook as the folder titled "Button" holding all your test cases.</p>
<h4 id="step3-writing-the-main-visual-tests">Step3: Writing the main visual tests</h4>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Default = <span class="hljs-function">() =&gt;</span> ({
    <span class="hljs-attr">Component</span>: Button,
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">value</span>: <span class="hljs-string">'Button'</span>
    }
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Success = <span class="hljs-function">() =&gt;</span> ({
    <span class="hljs-attr">Component</span>: Button,
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">value</span>: <span class="hljs-string">'Button'</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-string">'success'</span>
    }
})
</code></pre>
<p>You should see a screen just like this:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hx0qthu7f0xanvwytyi1.PNG" alt="Default button" /></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnrjbk2xqpy5hufsf8aw.PNG" alt="Success button" /></p>
<p>And your <code>button.stories.js</code> file should look like this: </p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/buttons/Button.svelte'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">title</span>: <span class="hljs-string">'Button'</span>,
    <span class="hljs-attr">component</span>: Button
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Default = <span class="hljs-function">() =&gt;</span> ({
    <span class="hljs-attr">Component</span>: Button,
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">value</span>: <span class="hljs-string">'Button'</span>
    }
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Success = <span class="hljs-function">() =&gt;</span> ({
    <span class="hljs-attr">Component</span>: Button,
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">value</span>: <span class="hljs-string">'Button'</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-string">'success'</span>
    }
})
</code></pre>
<p>Now that wraps up our button component library. The library flow should look like the image above. I also added some more visual tests to the <code>button.stories.js</code> file, the source code is available at the end of the tutorial.</p>
<h2 id="publish-to-npm">Publish to npm</h2>
<p>This is the easiest segment.</p>
<h3 id="step1-create-an-npm-account">Step1: Create an npm account</h3>
<p>Visit <a target="_blank" href="https://www.npmjs.com/">npmjs.com</a> and create an account if you haven't before.</p>
<h3 id="step2-login-to-npm-on-your-cli">Step2: Login to npm on your CLI</h3>
<pre><code class="lang-bash">npm login
</code></pre>
<p>You’ll be prompted to enter your username, password, and email address.
<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0kenk4i5em3t58eopw2b.PNG" alt="npm login" /></p>
<h3 id="step3-publish">Step3: Publish</h3>
<pre><code class="lang-bash">npm publish
</code></pre>
<p>NOTE: Your package name can be changed in the <code>package.json</code> file.</p>
<h2 id="conclusion">Conclusion</h2>
<p>With Svelte and Storybook, we were able to create a button component library with minimal effort.</p>
<p>The full source code of the app can be found here on <a target="_blank" href="https://github.com/Gift-Stack/svelte-button-library">Github</a>. Trying out Storybook was very interesting and I recommend you read the <a target="_blank" href="https://storybook.js.org/docs/svelte/get-started/introduction">documentation</a> to see the magical things you could do with it. If you have any questions, don't hesitate to hit me up on Twitter: <a target="_blank" href="https://twitter.com/thenameisgifted">@theNameIsGiFTED</a></p>
]]></content:encoded></item></channel></rss>