<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Brian</title>
        <link>https://paragraph.com/@corporationplaza</link>
        <description>undefined</description>
        <lastBuildDate>Wed, 15 Jul 2026 16:08:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Building a custom frontend for a Nina Protocol Hub]]></title>
            <link>https://paragraph.com/@corporationplaza/building-a-custom-frontend-for-a-nina-protocol-hub</link>
            <guid>aEcsp7M8XdJnwZBYUP4Z</guid>
            <pubDate>Sun, 04 Dec 2022 21:21:06 GMT</pubDate>
            <description><![CDATA[In this series of posts, I’m going to walk you though setting up a custom frontend for your Nina Protocol hub leveraging open source tools. There is really no right or wrong way to go about this, I’ll show you one way to do it but really this is meant to be more of a minimal jumping off point for continued experimentation. Some assumptions up front:You have a crypto wallet set up for SolanaYou have a hub on Nina with a few published releasesYou are comfortable writing JavaScript and have some...]]></description>
            <content:encoded><![CDATA[<p>In this series of posts, I’m going to walk you though setting up a custom frontend for your <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.ninaprotocol.com/">Nina Protocol</a> hub leveraging open source tools. There is really no right or wrong way to go about this, I’ll show you one way to do it but really this is meant to be more of a minimal jumping off point for continued experimentation.</p><p>Some assumptions up front:</p><ul><li><p>You have a crypto wallet set up for <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://solana.com/">Solana</a></p></li><li><p>You have a hub on Nina with a few published releases</p></li><li><p>You are comfortable writing JavaScript and have some experience with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactjs.org/">React</a> or something similar.</p></li></ul><p>Ok so first thing is first, lets create a new react app with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://create-react-app.dev/">create-react-app</a> and the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://create-react-app.dev/docs/adding-typescript/">TypeScript template</a>. I like TypeScript because it helps you avoid writing bugs and gives you much better autocomplete in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://code.visualstudio.com/">VSCode</a>.</p><p>I use <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://yarnpkg.com/">yarn</a> so in the terminal I’m going to run the command below to do the inital setup</p><pre data-type="codeBlock" text="yarn create react-app my-nina-hub --template typescript
"><code>yarn create react<span class="hljs-operator">-</span>app my<span class="hljs-operator">-</span>nina<span class="hljs-operator">-</span>hub <span class="hljs-operator">-</span><span class="hljs-operator">-</span>template typescript
</code></pre><p>That will provide you with the initial scaffolding for your app. Next, we’re going to want to install a bunch of dependencies so that we can connect to the users wallet and interact with Nina Protocol via the <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/nina-protocol/js-sdk">nina js-sdk</a>. I’m also going to throw in <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://styled-components.com/">styled-components</a> so that we can make the page look nice but you can use whatever you want for styling.</p><pre data-type="codeBlock" text="yarn add @nina-protocol/js-sdk @solana/wallet-adapter-wallets @solana/wallet-adapter-react @solana/wallet-adapter-base @solana/wallet-adapter-react-ui @solana/web3.js styled-components @types/styled-components
"><code>yarn add @nina<span class="hljs-operator">-</span>protocol<span class="hljs-operator">/</span>js<span class="hljs-operator">-</span>sdk @solana<span class="hljs-operator">/</span>wallet<span class="hljs-operator">-</span>adapter<span class="hljs-operator">-</span>wallets @solana<span class="hljs-operator">/</span>wallet<span class="hljs-operator">-</span>adapter<span class="hljs-operator">-</span>react @solana<span class="hljs-operator">/</span>wallet<span class="hljs-operator">-</span>adapter<span class="hljs-operator">-</span>base @solana<span class="hljs-operator">/</span>wallet<span class="hljs-operator">-</span>adapter<span class="hljs-operator">-</span>react<span class="hljs-operator">-</span>ui @solana<span class="hljs-operator">/</span>web3.js styled<span class="hljs-operator">-</span>components @types<span class="hljs-operator">/</span>styled<span class="hljs-operator">-</span>components
</code></pre><p>Now that we have our dependencies lets fire up the app and see what we’ve got.</p><pre data-type="codeBlock" text="yarn start
"><code>yarn <span class="hljs-keyword">start</span>
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/cfccd974ff5bb8fd55046c2ab32372b8b3140e1342d6d484cb82daddc079889e.png" alt="Sick" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Sick</figcaption></figure><p>Lets commit the changes we’ve made so far. <code>create-react-app</code> sets up git out of the box so we can use that to organize our change sets. You can view the commit on GitHub <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub/commit/9c16ad155abbb175123ec9475f246794f06cb328">here</a>.</p><pre data-type="codeBlock" text="git add . //add all changes
git commit -m &quot;set up app scaffold with dependencies&quot;
"><code>git <span class="hljs-keyword">add</span> . <span class="hljs-operator">/</span><span class="hljs-operator">/</span><span class="hljs-keyword">add</span> <span class="hljs-keyword">all</span> changes
git <span class="hljs-keyword">commit</span> <span class="hljs-operator">-</span>m "set up app scaffold with dependencies"
</code></pre><p>Alright, now lets delete a bunch of the boilerplate here so we have a totally clean slate to work with. You can look at <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub/commit/753ebb311bdfc982d02b4a9c29f612b86ccc9e61">this commit</a> to see everything I got rid of but, the important thing is you get you App component looking something like this</p><pre data-type="codeBlock" text="import React from &quot;react&quot;;

function App() {
  return &lt;div className=&quot;App&quot;&gt;Blank slate&lt;/div&gt;;
}

export default App;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"App"</span><span class="hljs-operator">></span>Blank slate<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>;
}

export default App;
</code></pre><p>Sweet. Now we have a blank canvas to work with to start building the frontend for our Nina hub. Lets start by creating a React <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://reactjs.org/docs/context.html">context</a> with a hook wrapper around the Nina SDK so that we can interact with the Nina protocol anywhere in our app.</p><p>I’m not going to go into super deep detail about how React works here, essentially we’re creating a context at the top level of our app which we will use to pull data from Nina. I’m going to create a file for this under <code>src/context/nina.tsx</code> and its going to start out pretty empty.</p><pre data-type="codeBlock" text="import * as React from &quot;react&quot;;

const NinaContext = React.createContext(null);

type NinaProviderProps = {
  children?: React.ReactNode;
};

function NinaProvider({ children }: NinaProviderProps) {
  return &lt;NinaContext.Provider value={null}&gt;{children}&lt;/NinaContext.Provider&gt;;
}

function useNina() {
  const context = React.useContext(NinaContext);
  if (context === undefined) {
    throw new Error(&quot;useNina must be used within a NinaProvider&quot;);
  }
  return context;
}

export { NinaProvider, useNina };
"><code><span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;

const NinaContext <span class="hljs-operator">=</span> React.createContext(null);

<span class="hljs-keyword">type</span> NinaProviderProps <span class="hljs-operator">=</span> {
  children?: React.ReactNode;
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NinaProvider</span>(<span class="hljs-params">{ children }: NinaProviderProps</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-operator">&#x3C;</span>NinaContext.Provider value<span class="hljs-operator">=</span>{null}<span class="hljs-operator">></span>{children}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>NinaContext.Provider>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useNina</span>(<span class="hljs-params"></span>) </span>{
  const context <span class="hljs-operator">=</span> React.useContext(NinaContext);
  <span class="hljs-keyword">if</span> (context <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> undefined) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"useNina must be used within a NinaProvider"</span>);
  }
  <span class="hljs-keyword">return</span> context;
}

export { NinaProvider, useNina };
</code></pre><p>Let’s import this in the App component</p><pre data-type="codeBlock" text="import React from &quot;react&quot;;
import { NinaProvider } from &quot;./context/nina&quot;;

function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;NinaProvider&gt;Blank Slate&lt;/NinaProvider&gt;
    &lt;/div&gt;
  );
}

export default App;
"><code><span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">NinaProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./context/nina"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>div className<span class="hljs-operator">=</span><span class="hljs-string">"App"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>NinaProvider<span class="hljs-operator">></span>Blank Slate<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>NinaProvider<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
  );
}

export default App;
</code></pre><p>Ok nice, so now we have a placeholder for our <code>NinaProvider</code> which is going to wrap our entire app. I’m going to commit the changes here, check out the change set on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub/commit/445914935199b70b194a0b80641c68a490d35e3e">GitHub</a>.</p><p>Now I want to set up some functions in the <code>NinaProvider</code> to start pulling in some data from my hub. Since I’m using typescript I need to create a <code>.d.ts</code> file to declare the types of the js-sdk package, so I made a file <code>src/types/js-sdk.d.ts</code> you’ll see below to make TypeScript happy. It would be nice to fill this out with actual types but for the sake of going fast I’ll just define it like this for now and maybe fill in more details later:</p><pre data-type="codeBlock" text="// src/types/js-sdk.d.ts

declare module &quot;@nina-protocol/js-sdk&quot; {
  const Nina: any;
  export = Nina;
}
"><code><span class="hljs-comment">// src/types/js-sdk.d.ts</span>

<span class="hljs-keyword">declare</span> <span class="hljs-variable language_">module</span> <span class="hljs-string">"@nina-protocol/js-sdk"</span> {
  <span class="hljs-keyword">const</span> <span class="hljs-title class_">Nina</span>: <span class="hljs-built_in">any</span>;
  <span class="hljs-keyword">export</span> = <span class="hljs-title class_">Nina</span>;
}
</code></pre><p>Ok now lets fetch the data from our hub. I’m going to use my hub id <code>‘corporationplaza’</code> and fetch the data in the <code>NinaProvider</code>. I’ll also add types for <code>Hub</code> and <code>Release</code> which will make writing our UI components a bit easier. <code>src/context/nina.tsx</code> now looks like this. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub/commit/a15990f2d9dec450404bed487233582a56699914">Here’s the commit</a> of my changes up to this point.</p><pre data-type="codeBlock" text="// src/context/nina.tsx 

import * as React from &quot;react&quot;;
import Nina from &quot;@nina-protocol/js-sdk&quot;;
import { useEffect, useState } from &quot;react&quot;;

Nina.client.init();

const NinaContext = React.createContext&lt;NinaContextValue&gt;({});

type NinaContextValue = {
  hub?: Hub;
  releases?: Release[];
};

type NinaProviderProps = {
  children?: React.ReactNode;
};

type Hub = {
  publicKey: string;
  handle: string;
  data: {
    displayName: string;
    description: string;
    externalUrl: string;
    image: string;
  };
  datetime: string;
  dataUri: string;
  authority: string;
};

type Release = {
  publicKey: string;
  mint: string;
  metadata: {
    name: string;
    symbol: string;
    description: string;
    image: string;
    animation_url: string;
    external_url: string;
    attributes: any[];
    collection: {
      name: string;
      family: string;
    };
    properties: {
      artist: string;
      title: string;
      date: Date;
      md5Digest: string;
      files: any[];
      category: string;
    };
    descriptionHtml: string;
  };
  datetime: Date;
  hubReleasePublicKey: string;
  visible: boolean;
  publishedThroughHub: string;
  publisher: string;
};

function NinaProvider({ children }: NinaProviderProps) {
  const [hub, setHub] = useState&lt;Hub&gt;();
  const [releases, setReleases] = useState&lt;Release[]&gt;();

  const fetchHub = async () =&gt; {
    const { hub, releases } = await Nina.Hub.fetch(&quot;corporationplaza&quot;);
    setHub(hub);
    setReleases(releases);
  };

  useEffect(() =&gt; {
    if (!hub) {
      fetchHub();
    }
  }, [hub]);

  console.log(hub);
  console.log(releases);

  const value = { hub, releases };
  return &lt;NinaContext.Provider value={value}&gt;{children}&lt;/NinaContext.Provider&gt;;
}

function useNina() {
  const context = React.useContext(NinaContext);
  if (context === undefined) {
    throw new Error(&quot;useNina must be used within a NinaProvider&quot;);
  }
  return context;
}

export { NinaProvider, useNina };
"><code>// src/context/nina.tsx 

import * as React from "react"<span class="hljs-comment">;</span>
import Nina from "@nina-protocol/js-sdk"<span class="hljs-comment">;</span>
import { useEffect, useState } from "react"<span class="hljs-comment">;</span>

Nina.client.init()<span class="hljs-comment">;</span>

const <span class="hljs-attr">NinaContext</span> = React.createContext&#x3C;NinaContextValue>({})<span class="hljs-comment">;</span>

type <span class="hljs-attr">NinaContextValue</span> = {
  hub?: Hub<span class="hljs-comment">;</span>
  releases?: Release<span class="hljs-section">[]</span><span class="hljs-comment">;</span>
}<span class="hljs-comment">;</span>

type <span class="hljs-attr">NinaProviderProps</span> = {
  children?: React.ReactNode<span class="hljs-comment">;</span>
}<span class="hljs-comment">;</span>

type <span class="hljs-attr">Hub</span> = {
  publicKey: string<span class="hljs-comment">;</span>
  handle: string<span class="hljs-comment">;</span>
  data: {
    displayName: string<span class="hljs-comment">;</span>
    description: string<span class="hljs-comment">;</span>
    externalUrl: string<span class="hljs-comment">;</span>
    image: string<span class="hljs-comment">;</span>
  }<span class="hljs-comment">;</span>
  datetime: string<span class="hljs-comment">;</span>
  dataUri: string<span class="hljs-comment">;</span>
  authority: string<span class="hljs-comment">;</span>
}<span class="hljs-comment">;</span>

type <span class="hljs-attr">Release</span> = {
  publicKey: string<span class="hljs-comment">;</span>
  mint: string<span class="hljs-comment">;</span>
  metadata: {
    name: string<span class="hljs-comment">;</span>
    symbol: string<span class="hljs-comment">;</span>
    description: string<span class="hljs-comment">;</span>
    image: string<span class="hljs-comment">;</span>
    animation_url: string<span class="hljs-comment">;</span>
    external_url: string<span class="hljs-comment">;</span>
    attributes: any<span class="hljs-section">[]</span><span class="hljs-comment">;</span>
    collection: {
      name: string<span class="hljs-comment">;</span>
      family: string<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    properties: {
      artist: string<span class="hljs-comment">;</span>
      title: string<span class="hljs-comment">;</span>
      date: Date<span class="hljs-comment">;</span>
      md5Digest: string<span class="hljs-comment">;</span>
      files: any<span class="hljs-section">[]</span><span class="hljs-comment">;</span>
      category: string<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    descriptionHtml: string<span class="hljs-comment">;</span>
  }<span class="hljs-comment">;</span>
  datetime: Date<span class="hljs-comment">;</span>
  hubReleasePublicKey: string<span class="hljs-comment">;</span>
  visible: boolean<span class="hljs-comment">;</span>
  publishedThroughHub: string<span class="hljs-comment">;</span>
  publisher: string<span class="hljs-comment">;</span>
}<span class="hljs-comment">;</span>

function NinaProvider({ children }: NinaProviderProps) {
  const <span class="hljs-section">[hub, setHub]</span> = useState&#x3C;Hub>()<span class="hljs-comment">;</span>
  const <span class="hljs-section">[releases, setReleases]</span> = useState&#x3C;Release<span class="hljs-section">[]</span>>()<span class="hljs-comment">;</span>

  const <span class="hljs-attr">fetchHub</span> = async () => {
    const { hub, releases } = await Nina.Hub.fetch("corporationplaza")<span class="hljs-comment">;</span>
    setHub(hub)<span class="hljs-comment">;</span>
    setReleases(releases)<span class="hljs-comment">;</span>
  }<span class="hljs-comment">;</span>

  useEffect(() => {
    if (!hub) {
      fetchHub()<span class="hljs-comment">;</span>
    }
  }, <span class="hljs-section">[hub]</span>)<span class="hljs-comment">;</span>

  console.log(hub)<span class="hljs-comment">;</span>
  console.log(releases)<span class="hljs-comment">;</span>

  const <span class="hljs-attr">value</span> = { hub, releases }<span class="hljs-comment">;</span>
  return &#x3C;NinaContext.Provider <span class="hljs-attr">value</span>={value}>{children}&#x3C;/NinaContext.Provider><span class="hljs-comment">;</span>
}

function useNina() {
  const <span class="hljs-attr">context</span> = React.useContext(NinaContext)<span class="hljs-comment">;</span>
  if (<span class="hljs-attr">context</span> === undefined) {
    throw new Error("useNina must be used within a NinaProvider")<span class="hljs-comment">;</span>
  }
  return context<span class="hljs-comment">;</span>
}

export { NinaProvider, useNina }<span class="hljs-comment">;</span>
</code></pre><p>Our console output now looks like this, you can see the hub data and releases coming back. Woohoo!</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2f9841409d72d9dd5689bc05936d447d86ed4ab37363f2b5ee6bcfb26391ec8d.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Alright, lets start getting some of this stuff showing in the UI. I’m going to create a <code>components</code> folder under <code>/src</code> and make two component files, <code>HubInfo.tsx</code> and <code>HubReleases.tsx</code> to display the info for each. I’ll just do some basic styling for now so we can see what it looks like on the page.</p><pre data-type="codeBlock" text="// src/components/HubInfo.tsx

import React from &quot;react&quot;;
import styled from &quot;styled-components&quot;;

import { useNina } from &quot;../context/nina&quot;;

function HubInfo() {
  const { hub } = useNina();

  if (!hub) {
    return null;
  }

  return (
    &lt;HubInfoContainer className=&quot;HubInfo&quot;&gt;
      &lt;HubImage src={hub.data.image} /&gt;
      &lt;div&gt;
        &lt;h1&gt;{hub.data.displayName}&lt;/h1&gt;
        &lt;h2&gt;{hub.data.description}&lt;/h2&gt;
        &lt;a href={hub.data.externalUrl} target=&quot;_blank&quot; rel=&quot;noreferrer&quot;&gt;
          view on nina
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/HubInfoContainer&gt;
  );
}
const HubInfoContainer = styled.div`
  display: flex;
  gap: 1em;
  margin: 1em;
`;

const HubImage = styled.img`
  max-width: 200px;
`;

export default HubInfo;
"><code><span class="hljs-comment">// src/components/HubInfo.tsx</span>

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">styled</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"styled-components"</span>;

<span class="hljs-keyword">import</span> { <span class="hljs-title">useNina</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../context/nina"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HubInfo</span>(<span class="hljs-params"></span>) </span>{
  const { hub } <span class="hljs-operator">=</span> useNina();

  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>hub) {
    <span class="hljs-keyword">return</span> null;
  }

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>HubInfoContainer className<span class="hljs-operator">=</span><span class="hljs-string">"HubInfo"</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>HubImage src<span class="hljs-operator">=</span>{hub.data.image} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>div<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>h1<span class="hljs-operator">></span>{hub.data.displayName}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h1<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span>{hub.data.description}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>a href<span class="hljs-operator">=</span>{hub.data.externalUrl} target<span class="hljs-operator">=</span><span class="hljs-string">"_blank"</span> rel<span class="hljs-operator">=</span><span class="hljs-string">"noreferrer"</span><span class="hljs-operator">></span>
          <span class="hljs-keyword">view</span> on nina
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>a<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>div<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>HubInfoContainer<span class="hljs-operator">></span>
  );
}
const HubInfoContainer <span class="hljs-operator">=</span> styled.div`
  display: flex;
  gap: 1em;
  margin: 1em;
`;

const HubImage <span class="hljs-operator">=</span> styled.img`
  max<span class="hljs-operator">-</span>width: 200px;
`;

export default HubInfo;
</code></pre><pre data-type="codeBlock" text="// src/components/HubReleases.tsx

import React from &quot;react&quot;;
import styled from &quot;styled-components&quot;;

import { useNina } from &quot;../context/nina&quot;;

function HubReleases() {
  const { releases } = useNina();
  if (!releases) {
    return null;
  }

  releases.sort(
    (a, b) =&gt; new Date(a.datetime).getTime() - new Date(b.datetime).getTime()
  );

  return (
    &lt;HubReleasesContainer&gt;
      {releases.map((release) =&gt; (
        &lt;HubReleaseContainer&gt;
          &lt;ReleaseImage src={release.metadata.image} /&gt;
          &lt;h2&gt;{release.metadata.name}&lt;/h2&gt;
          &lt;p&gt;{release.metadata.description}&lt;/p&gt;
        &lt;/HubReleaseContainer&gt;
      ))}
    &lt;/HubReleasesContainer&gt;
  );
}

const HubReleasesContainer = styled.div`
  margin: 1em;
`;

const HubReleaseContainer = styled.div`
  display: flex;
  gap: 1em;
  margin: 1em 0;
`;

const ReleaseImage = styled.img`
  max-width: 300px;
`;

export default HubReleases;
"><code><span class="hljs-comment">// src/components/HubReleases.tsx</span>

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">styled</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"styled-components"</span>;

<span class="hljs-keyword">import</span> { <span class="hljs-title">useNina</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../context/nina"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HubReleases</span>(<span class="hljs-params"></span>) </span>{
  const { releases } <span class="hljs-operator">=</span> useNina();
  <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>releases) {
    <span class="hljs-keyword">return</span> null;
  }

  releases.sort(
    (a, b) <span class="hljs-operator">=</span><span class="hljs-operator">></span> <span class="hljs-keyword">new</span> Date(a.datetime).getTime() <span class="hljs-operator">-</span> <span class="hljs-keyword">new</span> Date(b.datetime).getTime()
  );

  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>HubReleasesContainer<span class="hljs-operator">></span>
      {releases.map((release) <span class="hljs-operator">=</span><span class="hljs-operator">></span> (
        <span class="hljs-operator">&#x3C;</span>HubReleaseContainer<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>ReleaseImage src<span class="hljs-operator">=</span>{release.metadata.image} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span>{release.metadata.<span class="hljs-built_in">name</span>}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>{release.metadata.description}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>HubReleaseContainer<span class="hljs-operator">></span>
      ))}
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>HubReleasesContainer<span class="hljs-operator">></span>
  );
}

const HubReleasesContainer <span class="hljs-operator">=</span> styled.div`
  margin: 1em;
`;

const HubReleaseContainer <span class="hljs-operator">=</span> styled.div`
  display: flex;
  gap: 1em;
  margin: 1em <span class="hljs-number">0</span>;
`;

const ReleaseImage <span class="hljs-operator">=</span> styled.img`
  max<span class="hljs-operator">-</span>width: 300px;
`;

export default HubReleases;
</code></pre><p>And I’ll add these to the <code>App.tsx</code> component</p><pre data-type="codeBlock" text="// src/App.tsx

import React from &quot;react&quot;;
import HubInfo from &quot;./components/HubInfo&quot;;
import HubReleases from &quot;./components/HubReleases&quot;;
import { NinaProvider } from &quot;./context/nina&quot;;

function App() {
  return (
    &lt;NinaProvider&gt;
      &lt;HubInfo /&gt;
      &lt;HubReleases /&gt;
    &lt;/NinaProvider&gt;
  );
}

export default App;
"><code><span class="hljs-comment">// src/App.tsx</span>

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">HubInfo</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/HubInfo"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">HubReleases</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/HubReleases"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">NinaProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./context/nina"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>NinaProvider<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>HubInfo <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>HubReleases <span class="hljs-operator">/</span><span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>NinaProvider<span class="hljs-operator">></span>
  );
}

export default App;
</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/1b215b1c0a3015534b911c51689cb3c4c6fc6f953b51c19c08b9aa8eafe994ec.png" alt="Wow, look at that!" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Wow, look at that!</figcaption></figure><p>Looking good! We’ve got the hub info showing up along with all the releases from the hub, sorted oldest first. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub/commit/53986b6154cd8d4c22b29756c522a0a0fd1bf2eb">Here’s the commit</a>.</p><p>Finally, lets create an audio player so that when a user clicks on a release, they can listen to the track and play/pause. I’m going to create a context for this as well to make reading and writing the player state relatively simple across the app. I’m also going to move the types for <code>Release</code> and <code>Hub</code> out of the Nina context and into their own file to separate those concerns and import them for use in the player.</p><p>I’ll move the types out of <code>src/context/nina.tsx</code> to a new file <code>src/types/ninaTypes.ts</code></p><pre data-type="codeBlock" text="// src/types/ninaTypes.ts

export type Hub = {
  publicKey: string;
  handle: string;
  data: {
    displayName: string;
    description: string;
    externalUrl: string;
    image: string;
  };
  datetime: string;
  dataUri: string;
  authority: string;
};

export type Release = {
  publicKey: string;
  mint: string;
  metadata: {
    name: string;
    symbol: string;
    description: string;
    image: string;
    animation_url: string;
    external_url: string;
    attributes: any[];
    collection: {
      name: string;
      family: string;
    };
    properties: {
      artist: string;
      title: string;
      date: Date;
      md5Digest: string;
      files: any[];
      category: string;
    };
    descriptionHtml: string;
  };
  datetime: Date;
  hubReleasePublicKey: string;
  visible: boolean;
  publishedThroughHub: string;
  publisher: string;
};
"><code>// src/types/ninaTypes.ts

export type <span class="hljs-attr">Hub</span> = {
  publicKey: string<span class="hljs-comment">;</span>
  handle: string<span class="hljs-comment">;</span>
  data: {
    displayName: string<span class="hljs-comment">;</span>
    description: string<span class="hljs-comment">;</span>
    externalUrl: string<span class="hljs-comment">;</span>
    image: string<span class="hljs-comment">;</span>
  }<span class="hljs-comment">;</span>
  datetime: string<span class="hljs-comment">;</span>
  dataUri: string<span class="hljs-comment">;</span>
  authority: string<span class="hljs-comment">;</span>
}<span class="hljs-comment">;</span>

export type <span class="hljs-attr">Release</span> = {
  publicKey: string<span class="hljs-comment">;</span>
  mint: string<span class="hljs-comment">;</span>
  metadata: {
    name: string<span class="hljs-comment">;</span>
    symbol: string<span class="hljs-comment">;</span>
    description: string<span class="hljs-comment">;</span>
    image: string<span class="hljs-comment">;</span>
    animation_url: string<span class="hljs-comment">;</span>
    external_url: string<span class="hljs-comment">;</span>
    attributes: any<span class="hljs-section">[]</span><span class="hljs-comment">;</span>
    collection: {
      name: string<span class="hljs-comment">;</span>
      family: string<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    properties: {
      artist: string<span class="hljs-comment">;</span>
      title: string<span class="hljs-comment">;</span>
      date: Date<span class="hljs-comment">;</span>
      md5Digest: string<span class="hljs-comment">;</span>
      files: any<span class="hljs-section">[]</span><span class="hljs-comment">;</span>
      category: string<span class="hljs-comment">;</span>
    }<span class="hljs-comment">;</span>
    descriptionHtml: string<span class="hljs-comment">;</span>
  }<span class="hljs-comment">;</span>
  datetime: Date<span class="hljs-comment">;</span>
  hubReleasePublicKey: string<span class="hljs-comment">;</span>
  visible: boolean<span class="hljs-comment">;</span>
  publishedThroughHub: string<span class="hljs-comment">;</span>
  publisher: string<span class="hljs-comment">;</span>
}<span class="hljs-comment">;</span>
</code></pre><p>Then, set up the audio player and dock it to the bottom right of the page.</p><pre data-type="codeBlock" text="// src/context/player.tsx

import * as React from &quot;react&quot;;
import { useEffect, useRef, useState } from &quot;react&quot;;
import styled from &quot;styled-components&quot;;
import { Release } from &quot;../types/ninaTypes&quot;;

const PlayerContext = React.createContext&lt;PlayerContextValue&gt;({
  setCurrentTrack: () =&gt; null,
  playing: false,
});

type PlayerContextValue = {
  currentTrack?: Release;
  setCurrentTrack: (release: Release) =&gt; void;
  playing: boolean;
};

type PlayerProviderProps = {
  children?: React.ReactNode;
};

function PlayerProvider({ children }: PlayerProviderProps) {
  const [currentTrack, setCurrentTrack] = useState&lt;Release&gt;();
  const [playing, setPlaying] = useState(false);
  const playerRef = useRef&lt;HTMLAudioElement&gt;(null);

  useEffect(() =&gt; {
    if (currentTrack) {
      playerRef.current?.play();
      setPlaying(true);
    }
  }, [currentTrack]);

  const value = { currentTrack, setCurrentTrack, playing };
  return (
    &lt;PlayerContext.Provider value={value}&gt;
      {children}
      {currentTrack &amp;&amp; (
        &lt;audio src={currentTrack?.metadata?.animation_url} ref={playerRef} /&gt;
      )}
      &lt;PlayerContainer&gt;
        &lt;Cover src={currentTrack?.metadata?.image} /&gt;
        &lt;TrackTitle&gt;{currentTrack?.metadata?.properties?.title}&lt;/TrackTitle&gt;
        &lt;button
          onClick={() =&gt; {
            if (playing) {
              playerRef?.current?.pause();
            } else {
              playerRef?.current?.play();
            }
            setPlaying(!playing);
          }}
        &gt;
          {playing ? &quot;⏸️&quot; : &quot;▶️&quot;}
        &lt;/button&gt;
      &lt;/PlayerContainer&gt;
    &lt;/PlayerContext.Provider&gt;
  );
}

const PlayerContainer = styled.div`
  display: flex;
  flex-direction: row;
  gap: 20px;
  align-items: center;
  position: fixed;
  padding: 10px;
  bottom: 0;
  right: 0;
  top: unset;
  background-color: white;
`;

const TrackTitle = styled.h3``;

const Cover = styled.img`
  width: 30px;
  height: 30px;
`;

function usePlayer() {
  const context = React.useContext(PlayerContext);
  if (context === undefined) {
    throw new Error(&quot;usePlayer must be used within a PlayerProvider&quot;);
  }
  return context;
}

export { PlayerProvider, usePlayer };
"><code><span class="hljs-comment">// src/context/player.tsx</span>

<span class="hljs-keyword">import</span> <span class="hljs-operator">*</span> <span class="hljs-title"><span class="hljs-keyword">as</span></span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">useEffect</span>, <span class="hljs-title">useRef</span>, <span class="hljs-title">useState</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">styled</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"styled-components"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">Release</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"../types/ninaTypes"</span>;

const PlayerContext <span class="hljs-operator">=</span> React.createContext&#x3C;PlayerContextValue<span class="hljs-operator">></span>({
  setCurrentTrack: () <span class="hljs-operator">=</span><span class="hljs-operator">></span> null,
  playing: <span class="hljs-literal">false</span>,
});

<span class="hljs-keyword">type</span> PlayerContextValue <span class="hljs-operator">=</span> {
  currentTrack?: Release;
  setCurrentTrack: (release: Release) <span class="hljs-operator">=</span><span class="hljs-operator">></span> void;
  playing: boolean;
};

<span class="hljs-keyword">type</span> PlayerProviderProps <span class="hljs-operator">=</span> {
  children?: React.ReactNode;
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PlayerProvider</span>(<span class="hljs-params">{ children }: PlayerProviderProps</span>) </span>{
  const [currentTrack, setCurrentTrack] <span class="hljs-operator">=</span> useState<span class="hljs-operator">&#x3C;</span>Release<span class="hljs-operator">></span>();
  const [playing, setPlaying] <span class="hljs-operator">=</span> useState(<span class="hljs-literal">false</span>);
  const playerRef <span class="hljs-operator">=</span> useRef<span class="hljs-operator">&#x3C;</span>HTMLAudioElement<span class="hljs-operator">></span>(null);

  useEffect(() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
    <span class="hljs-keyword">if</span> (currentTrack) {
      playerRef.current?.play();
      setPlaying(<span class="hljs-literal">true</span>);
    }
  }, [currentTrack]);

  const value <span class="hljs-operator">=</span> { currentTrack, setCurrentTrack, playing };
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>PlayerContext.Provider value<span class="hljs-operator">=</span>{value}<span class="hljs-operator">></span>
      {children}
      {currentTrack <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> (
        <span class="hljs-operator">&#x3C;</span>audio src<span class="hljs-operator">=</span>{currentTrack?.metadata?.animation_url} ref<span class="hljs-operator">=</span>{playerRef} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      )}
      <span class="hljs-operator">&#x3C;</span>PlayerContainer<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>Cover src<span class="hljs-operator">=</span>{currentTrack?.metadata?.image} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>TrackTitle<span class="hljs-operator">></span>{currentTrack?.metadata?.properties?.title}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>TrackTitle<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>button
          onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
            <span class="hljs-keyword">if</span> (playing) {
              playerRef?.current?.pause();
            } <span class="hljs-keyword">else</span> {
              playerRef?.current?.play();
            }
            setPlaying(<span class="hljs-operator">!</span>playing);
          }}
        <span class="hljs-operator">></span>
          {playing ? <span class="hljs-string">"⏸️"</span> : <span class="hljs-string">"▶️"</span>}
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>PlayerContainer<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>PlayerContext.Provider>
  );
}

const PlayerContainer <span class="hljs-operator">=</span> styled.div`
  display: flex;
  flex<span class="hljs-operator">-</span>direction: row;
  gap: 20px;
  align<span class="hljs-operator">-</span>items: center;
  position: <span class="hljs-keyword">fixed</span>;
  padding: 10px;
  bottom: <span class="hljs-number">0</span>;
  right: <span class="hljs-number">0</span>;
  top: unset;
  background<span class="hljs-operator">-</span>color: white;
`;

const TrackTitle <span class="hljs-operator">=</span> styled.h3``;

const Cover <span class="hljs-operator">=</span> styled.img`
  width: 30px;
  height: 30px;
`;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">usePlayer</span>(<span class="hljs-params"></span>) </span>{
  const context <span class="hljs-operator">=</span> React.useContext(PlayerContext);
  <span class="hljs-keyword">if</span> (context <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> undefined) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"usePlayer must be used within a PlayerProvider"</span>);
  }
  <span class="hljs-keyword">return</span> context;
}

export { PlayerProvider, usePlayer };
</code></pre><p>Add the player provider to <code>App.tsx</code></p><pre data-type="codeBlock" text="// src/App.tsx

import React from &quot;react&quot;;
import HubInfo from &quot;./components/HubInfo&quot;;
import HubReleases from &quot;./components/HubReleases&quot;;
import { NinaProvider } from &quot;./context/nina&quot;;
import { PlayerProvider } from &quot;./context/player&quot;;

function App() {
  return (
    &lt;NinaProvider&gt;
      &lt;PlayerProvider&gt;
        &lt;HubInfo /&gt;
        &lt;HubReleases /&gt;
      &lt;/PlayerProvider&gt;
    &lt;/NinaProvider&gt;
  );
}

export default App;
"><code><span class="hljs-comment">// src/App.tsx</span>

<span class="hljs-keyword">import</span> <span class="hljs-title">React</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">HubInfo</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/HubInfo"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-title">HubReleases</span> <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./components/HubReleases"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">NinaProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./context/nina"</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title">PlayerProvider</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">"./context/player"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&#x3C;</span>NinaProvider<span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span>PlayerProvider<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>HubInfo <span class="hljs-operator">/</span><span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span>HubReleases <span class="hljs-operator">/</span><span class="hljs-operator">></span>
      <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>PlayerProvider<span class="hljs-operator">></span>
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>NinaProvider<span class="hljs-operator">></span>
  );
}

export default App;
</code></pre><p>And update the <code>HubReleases</code> component to set the current track on click.</p><pre data-type="codeBlock" text="// src/components/HubReleases.tsx

function HubReleases() {
  const { releases } = useNina();
  const { setCurrentTrack } = usePlayer();

...

    &lt;HubReleasesContainer&gt;
      {releases.map((release) =&gt; (
        &lt;HubReleaseContainer
          onClick={() =&gt; {
            setCurrentTrack(release);
          }}
        &gt;
          &lt;ReleaseImage src={release.metadata.image} /&gt;
          &lt;h2&gt;{release.metadata.name}&lt;/h2&gt;
          &lt;p&gt;{release.metadata.description}&lt;/p&gt;
        &lt;/HubReleaseContainer&gt;
      ))}
    &lt;/HubReleasesContainer&gt;

...
"><code><span class="hljs-comment">// src/components/HubReleases.tsx</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HubReleases</span>(<span class="hljs-params"></span>) </span>{
  const { releases } <span class="hljs-operator">=</span> useNina();
  const { setCurrentTrack } <span class="hljs-operator">=</span> usePlayer();

...

    <span class="hljs-operator">&#x3C;</span>HubReleasesContainer<span class="hljs-operator">></span>
      {releases.map((release) <span class="hljs-operator">=</span><span class="hljs-operator">></span> (
        <span class="hljs-operator">&#x3C;</span>HubReleaseContainer
          onClick<span class="hljs-operator">=</span>{() <span class="hljs-operator">=</span><span class="hljs-operator">></span> {
            setCurrentTrack(release);
          }}
        <span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>ReleaseImage src<span class="hljs-operator">=</span>{release.metadata.image} <span class="hljs-operator">/</span><span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>h2<span class="hljs-operator">></span>{release.metadata.<span class="hljs-built_in">name</span>}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>h2<span class="hljs-operator">></span>
          <span class="hljs-operator">&#x3C;</span>p<span class="hljs-operator">></span>{release.metadata.description}<span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>p<span class="hljs-operator">></span>
        <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>HubReleaseContainer<span class="hljs-operator">></span>
      ))}
    <span class="hljs-operator">&#x3C;</span><span class="hljs-operator">/</span>HubReleasesContainer<span class="hljs-operator">></span>

...
</code></pre><p>Commit of these changes <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub/commit/c59045bd39a51a761edaa31a860f16526b868239">here</a>.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2ff79608ab0ec9362acea620686c634b60f141417565ca924deac1ff1f229341.png" alt="Look at that, we&apos;re in business!" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Look at that, we&apos;re in business!</figcaption></figure><p>Ta da! Our own custom frontend. I’m going to take a break here to kick back and listen to these tunes. In my next post, I’ll show you how to let users connect their crypto wallet so they can see which tracks they own and allow users to purchase releases directly from this page. You can find the complete code <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/brianchitester/my-nina-hub">here</a> and view the site live at</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://my-nina-hub.netlify.app/">https://my-nina-hub.netlify.app/</a></p>]]></content:encoded>
            <author>corporationplaza@newsletter.paragraph.com (Brian)</author>
        </item>
    </channel>
</rss>