Strapi
Published in

Strapi

How to Build a Blog App Using Blazor WASM and Strapi

Learn how to create a blazing-fast blog app using Blazor WebAssembly, Tailwind CSS, and Strapi. We will be using Strapi to store our posts and Blazor for the front-end.

Prerequisites

Introduction

What is Blazor?

What is Strapi?

Back-end Setup

Step 1: Scaffold a Strapi project

$ mkdir purplerhino
$ cd purplerhino
/purplerhino $ git init
/purplerhino $ npx create-strapi-app backend --quickstart
Strapi Admin Registration

Step 2: Build the Posts collection

Build Post Content-Type
Final Post Content-Type
Posts collection in Strapi Dashboard

Step 3: Make Strapi API public

Select Public User Permission Roles
Open access for Posts

Step 4: Install and enable the Transformer plugin

/purplerhino/backend $ npm install strapi-plugin-transformer
/purplerhino/backend $ touch config/plugins.js
module.exports = ({ env }) => ({
'transformer': {
enabled: true,
config: {
prefix: '/api/',
responseTransforms: {
removeAttributesKey: true,
removeDataKey: true,
}
}
},
});
/purplerhino/backend $ yarn build
/purplerhino/backend $ yarn develop

Front-end Setup

Step 5: Setting up the Blazor project

/purplerhino $ dotnet new blazorwasm -o frontend
/purplerhino $ cd frontend
/purplerhino/frontend $ dotnet run
Blazor front-end app

Step 6: Integrate Strapi API with Blazor App

/purplerhino/frontend $ touch wwwroot/appsettings.json
{
"AppSettings": {
"STRAPI_API_URL": "http://localhost:1337"
}
}
/purplerhino/frontend $ mkdir Models
/purplerhino/frontend $ touch Models/AppSettings.cs
// ./Models/AppSettings.cs

namespace frontend.Models
{
public class AppSettings
{
public string STRAPI_API_URL { get; set; }
}
}
// ./Program.cs

using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using frontend.Models;
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace frontend
{
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();
}

public static void ConfigureServices(IServiceCollection services)
{
// Example of loading a configuration as configuration isn't available yet at this stage.
services.AddSingleton(provider =>
{
var config = provider.GetService<IConfiguration>();
return config.GetSection("App").Get<AppSettings>();
});
}
}
}

Step 7: Display all blog posts on the home page

@* ./Pages/Index.razor *@

@page "/"
@inject HttpClient Http
@using Microsoft.Extensions.Configuration;
@using Models
@inject IConfiguration Configuration

@if (allPosts == null)
{
<p><em>Loading...</em></p>
}
else
{
<section class="text-gray-600 body-font">
<div class="container px-5 py-4 mx-auto">
<div class="text-center mb-20">
<h1 class="sm:text-3xl text-2xl font-medium title-font text-gray-900 mb-4">Strapi Blazor Blog App</h1>
<p class="text-base leading-relaxed xl:w-2/4 lg:w-3/4 mx-auto text-gray-500s">British History - From the Anglo-Saxon era to present day Britain.</p>
<div class="flex mt-6 justify-center">
<div class="w-16 h-1 rounded-full bg-indigo-500 inline-flex"></div>
</div>
</div>
<div class="flex flex-wrap -m-3">
@foreach (var post in allPosts.data)
{
<div class="xl:w-1/4 md:w-1/2 p-4">
<div class="bg-gray-100 p-6 rounded-lg">
<img class="h-40 rounded w-full object-cover object-center mb-6" src="@post.Image.Url"
alt="content">
<h2 class="text-lg text-gray-900 font-medium title-font mb-4">@post.Title</h2>
<NavLink href="@($"post/{post.Id.ToString()}")">
<a class="text-indigo-500 inline-flex items-center">
Read More
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" class="w-4 h-4 ml-2" viewBox="0 0 24 24">
<path d="M5 12h14M12 5l7 7-7 7"></path>
</svg>
</a>
</NavLink>
</div>
</div>
}
</div>
</div>
</section>
}


@code {
private PostList allPosts = null;
public string strapi_api_url;

protected override async Task OnInitializedAsync()
{
strapi_api_url = Configuration["AppSettings:STRAPI_API_URL"];
var url = "{STRAPI_API_URL}/api/posts?populate=*";
allPosts = await Http.GetFromJsonAsync<PostList>(url.Replace("{STRAPI_API_URL}", strapi_api_url));
if (allPosts.data != null)
{
foreach (var post in allPosts.data)
{
post.Image.Url = strapi_api_url + post.Image.Url;
}
}
}

public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public Image Image { get; set; }
}

public class Image
{
public string Url { get; set; }
}

public class PostList
{
public List<Post> data { get; set; }
}

}
@* ./Shared/NavMenu.razor *@

<header class="text-gray-600 body-font">
<div class="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
<a class="flex order-first lg:order-none lg:w-1/5 title-font font-medium items-center text-gray-900 lg:items-center lg:justify-center mb-4 md:mb-0">
<img src="/blazor_strapi_logo.png" alt="logo" style="height:60px" />
<span class="ml-3 text-3xl">StrapiBlazorBlog</span>
</a>
</div>
</header>
@* ./Shared/MainLayout.razor *@

@inherits LayoutComponentBase

<div>
<NavMenu />

<div>
@Body
</div>
</div>
Blog App Home page

Step 8: Display a single blog post

/purplerhino/frontend $ touch Pages/PostDetails.razor
@* ./Pages/PostDetails.razor *@

@page "/post/{Id}"
@inject HttpClient Http
@inject NavigationManager NavigationManager
@using System.Text.Json.Serialization
@using Microsoft.Extensions.Configuration;
@using Models
@inject IConfiguration Configuration

@if (postDetails == null)
{
<p><em>Loading...</em></p>
}
else
{
<section class="text-gray-700 body-font">
<div class="container mx-auto flex px-5 pb-24 items-center justify-center flex-col">
<h1 class="title-font sm:text-4xl text-3xl mb-4 font-medium text-gray-900">@postDetails.Data.Title</h1>
<img class=" mb-10 object-cover object-center rounded" alt="hero" src="@postDetails.Data.Image.Url" style="height:400px;width:900px">
<div class=" w-full">
<div class="mb-8 leading-relaxed">@((MarkupString)postDetails.Data.Content)</div>
</div>
<div class="p-2 w-full">
<button class="flex mx-auto text-white bg-indigo-500 border-0 py-2 px-8 focus:outline-none hover:bg-indigo-600 rounded text-lg" @onclick="NavigateToIndexComponent">Back</button>
</div>
</div>
</section>
}

@code {
[Parameter] public string Id { get; set; }

private PostSingle postDetails = null;

public string strapi_api_url;

protected override async Task OnInitializedAsync()
{

strapi_api_url = Configuration["AppSettings:STRAPI_API_URL"];
var url = "{STRAPI_API_URL}/api/posts/{Id}?populate=*";
url = url.Replace("{STRAPI_API_URL}", strapi_api_url);
url = url.Replace("{Id}", Id);
postDetails = await Http.GetFromJsonAsync<PostSingle>(url);

if (postDetails.Data != null)
{
postDetails.Data.Image.Url = strapi_api_url + postDetails.Data.Image.Url;
}
}

private void NavigateToIndexComponent()
{
NavigationManager.NavigateTo("");
}

public class PostSingle
{
public Data Data { get; set; }
}
public class Data
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string Author { get; set; }
public Image Image { get; set; }
}

public class Image
{
public string Url { get; set; }
}

}
Single post page

Step 9: Deploy the blog app

Conclusion

--

--

Strapi is the leading open-source headless CMS. It’s 100% Javascript, fully customizable and developer-first. Unleash your content with Strapi.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store