This section is a template for the displayable content of the main app component. Using an {#if}
block to test whether the reactive variable loading
is true, this section displays a spinner until the backend can be accessed. Once the UI is connected to the backend, it shows some boilerplate text telling you to add something meaningful to the template.
<style>
section<style>
main {
text-align: center;
padding: 1em;
max-width: 240px;
margin: 0 auto;
}
@media (min-width: 640px) {
main {
max-width: none;
}
}
</style>
This section is a template for the CSS styles that get applied to the HTML in the <main>
section of the component. You can also use reactive variables here, and the styling will update whenever the variables change. These scaffolded styles set the component up with some basic layout to make it readable at small and large window sizes.
All Svelte components follow this general pattern. App.svelte
has special status as the root component, but otherwise it’s just like any other component.
First you’ll be adding a list of posts to the app, which means the components called AllPosts.svelte
needs to be imported.
At the top of the file, there is a list of scripts that are imported. Following the instructions that the scaffolding tool and the two conductor windows gave you, copy the following text and paste it into the script block of the App.svelte
file, on the line below import { clientContext } from './contexts';
import AllPosts from './forum/posts/AllPosts.svelte';
Next, add the component to the markup template in the <main>
section of the file, where the “EDIT ME!” content now lives. Remove everything inside the div
element that starts with this tag:
<div id="content" style="display: flex; flex-direction: column; flex: 1;">
and replace it with this line:
<AllPosts></AllPosts>
Your <main>
block should now look like this:
<main>
{#if loading}
<div style="display: flex; flex: 1; align-items: center; justify-content: ce
nter">
<mwc-circular-progress indeterminate />
</div>
{:else}
<div id="content" style="display: flex; flex-direction: column; flex: 1;">
<AllPosts></AllPosts>
</div>
{/if}
</main>
Svelte component tags
The AllPosts
element is obviously not standard HTML. In Svelte, each component has a correspondingly named custom element that will get replaced by the rendered component’s markup wherever it appears in another component’s template.
Save that file and take a look again at the two UI windows. They should both say ‘No posts found’.
Let’s fix that by adding the post creation component to the UI so we can add our first post. Import the CreatePost.svelte
component by adding this line in the script section, just below the AllPosts
component you previously imported:
import CreatePost from './forum/posts/CreatePost.svelte';
Add this new component to the <main>
block above the component you added:
<CreatePost></CreatePost>
Now your <main>
block should look like this:
<main>
{#if loading}
<div style="display: flex; flex: 1; align-items: center; justify-content: ce
nter">
<mwc-circular-progress indeterminate />
</div>
{:else}
<div id="content" style="display: flex; flex-direction: column; flex: 1;">
<CreatePost></CreatePost>
<AllPosts></AllPosts>
</div>
{/if}
</main>
Save the file and switch to one of the two conductor windows. You should now see a post form.
Type something into one of the two conductor windows like:
Hi from Alice
Hello Bob!
and then press the “Create Post” button.
You’ll immediately notice that the AllPosts
component has changed from saying “No posts found” to showing the newly created post. And if you take a look at the Holochain Playground window, you will see that two new actions have been created. If you click the App
element that’s appeared in Alice’s source chain, it will pull up some details in the Entry Contents section, including the title and content of Alice’s forum post. Note the hash of that entry (top of the Entry Contents window). Then click on the Create
action that’s pointing toward that App
entry in the source chain. If you look back at the contents window, you will see that it is now sharing the contents of the action. And if you look down the list a bit, you will see the hash of the entry for the first post.
At this point, in our DHT graph it should look like we have two different agents and then a separate floating entry and action. But we know that the new post is associated with a source chain which is associated with an agent. So why aren’t they connected on the DHT?
A source chain merely serves as a history of one agent’s attempts to manipulate the state of the graph database contained in the DHT. It’s useful to think of the DHT as a completely separate data store that doesn’t necessarily reflect agent-to-entry relationships unless you explicitly create a link type for them.
For the purpose of this hApp, we’re not interested in agent-to-posts relationships, so it’s fine that they’re not linked. But if you wanted to create a page that showed all posts by an author, that’s when you might want to scaffold that link type. hc scaffold collection
will do this for you if you choose a by-author collection, and will also create a get_posts_by_author
function.
You may also notice that only Alice’s UI showed the new post, while Bob’s didn’t. Just as with a traditional web app, database changes don’t automatically send out a notification to everyone who is interested. (Alice’s UI sees the changes because it knows how to update its own state for local changes.) You can create this functionality using a feature called signals, but let’s keep things simple for now. Right-click anywhere in Bob’s UI then choose “Reload” from the menu, and you’ll see that the changes have been copied from Alice’s app instance to Bob’s — all without a database server in the middle!
Let’s edit that post. In Alice’s UI window, click the edit adjacent to the post content (it should look like a pencil icon). The post content will be replaced by an editing form.
Now alter the content a bit. Maybe change it from Hello Bob!
to Hello, World!
and click “Save”.
That should update the post (at least for Alice). Bob’s UI will show the updated version the next time it’s reloaded.
If you look at the Holochain Playground, you should see that the update was added to Alice’s source chain. Specifically, it created:
Hello, World!
text),Update
action that indicated this entry is to replace the original entry, andCreateLink
action that connects the original create action to the update action.As explained previously, the original forum post already has a ‘link’ of sorts pointing from its action to the Update
action, which can be accessed when the original is retrieved. The extra link created by the CreateLink
action is optional — it merely speeds up retrieval when an action has been edited many times and has a long chain of update links, by allowing you to jump to the end of the chain. In the screenshot above, the link is highlighted in the DHT pane.
Now it’s time to add commenting to your app.
Previously, you added new components to the App.svelte
component. That made sense because posts were a global data type. But comments are related to a post, so from now on you’ll be modifying the PostDetail.svelte
component instead.
Open up PostDetail.svelte
in your IDE:
code ui/src/forum/posts/PostDetail.svelte
Just as before, first you’ll need to import the components near the top of the file (just after the line that imports EditPost.svelte
):
import CreateComment from './CreateComment.svelte';
import CommentsForPost from './CommentsForPost.svelte';
Further down the file, in the template block, add the components’ elements to the template. Put them both before the closing </div>
tag.
Here, the comment components need to know what post they’re related to. The post hash is the unique ID for the post, and the comment components’ elements both expect a postHash
attribute. This hash is available in the PostDetail
component as a variable of the same name, so it can be passed to the comment widgets.
<CreateComment postHash="{postHash}"></CreateComment>
<CommentsForPost postHash="{postHash}"></CommentsForPost>
Save the file, then go back to the UI windows to see the changes. Try typing in a comment or two, then deleting them. (You may need to refresh the UI windows to see the changes to the content.) Watch the Playground — see how the authors’ source chains and the graph in the DHT change as new information is added. The deleted comments are still there and can be accessed by code in your zomes if needed, but neither the application backend (that is, the functions defined in the coordinator zome) nor the UI have the capacity to show them.
Now that you’ve built an application, it’s time to get it into other people’s hands. You specify the components of a hApp using manifest files, written in YAML, and the hc
CLI looks for them when it’s building a distributable hApp for you. If you look in the workdir
folder:
ls workdir
You’ll see that the scaffolding tool has generated two manifest files for you:
happ.yaml web-happ.yaml
The first step is to package your app:
npm run package
This command does a number of things:
hc
CLI too to combine the built zomes and the DNA manifest into a .dna
file,.happ
file,.zip
file, and.webhapp
file.Of course, this application only has one zome and one DNA, but more complex apps may have many of each.
Now you’ll see some new files in workdir
:
ls workdir
happ.yaml my_forum_app.happ my_forum_app.webhapp web-happ.yaml
The packed app is now ready for deployment to a Holochain runtime.
In the centralized world, deployment is usually achieved by Continuous Integration (CI) automation that builds up code changes and sends them to whatever server or cloud-based platform you’re using. In the decentralized world of Holochain, deployment happens when end-users download and run your hApp in the Holochain runtime.
From the end-user perspective there are currently there are two ways to go about this, both of which will feel familiar:
Holochain’s official end-user runtime is the Holochain Launcher. It allows people to install apps from a built-in app store or from the filesystem. Installed apps can then be launched from a friendly UI. Note that the app store is itself a distributed Holochain application which provides details on applications that are available for download. As a developer you can either go through a simple publishing process and add your app to the app store where it will be available for installation by all people who use the Launcher, or you can share your application directly with end-users through your own channels and they can install it into their Holochain Launcher manually from the file system.
You can try this latter approach immediately by downloading and running the Launcher!
The steps for publishing an app to the Launcher’s app store are documented in the Github repository of the Holochain Launcher here.
If you prefer to distribute your app as a full standalone executable, you will need to bundle the Holochain runtime and your app together and take care of the necessary interactions between them. Because Holochain itself is really just a set of Rust libraries, you can of course build your own application that uses those libraries, but that’s a fair amount of work. Currently there are two much simpler paths for doing this: using either the Electron or Tauri frameworks, both of which can generate cross-platform executables from standard web UIs. These frameworks also support inclusion of additional binaries, which in our case are the holochain conductor and the lair keystore. Though there is quite a bit of complexity in setting things up for these frameworks, all the hard work has already been done for you:
Both of these are GitHub template repos with detailed instructions on how to clone the repos and add in your UI and DNA, as well as build and release commands that will create the cross-platform executables that you can then deliver to your end users.
Code Signing
For macOS and Windows, you will probably also want to go through the process of registering as a developer so that your application can be “code-signed”. This is needed so that users don’t get the “unsigned code” warnings when launching the applications on those platforms. Both of the above templates include instructions for CI automation to run the code-signing steps on release once you have acquired the necessary certificates.
Congratulations! You’ve learned how to create a new Holochain application, understand its layout, work with core concepts, and deploy and test the application.
Now that you have a basic understanding of Holochain development, you can continue exploring more advanced topics, such as:
Now that you have successfully built a basic forum application using Holochain and integrated it with a frontend, you may want to explore more advanced topics and techniques to further enhance your application or create new ones. Here are some resources and ideas to help you get started:
The official Holochain developer documentation is a valuable resource for deepening your understanding of Holochain concepts, techniques, and best practices. Be sure to explore the documentation thoroughly:
The Holochain community is an excellent source of support, inspiration, and collaboration. Consider engaging with the community to further your learning and development:
Studying existing Holochain applications and tutorials can provide valuable insights and inspiration for your projects. Here are some resources to explore: