Just 2 Simple Steps to Permanently Add Social Feed Features Like 'Moments' to Your Static Blog

Adding a “Moments” or social feed page to a static blog is a long-discussed topic. Previous solutions were either cumbersome to set up or inconvenient to use. To solve this problem definitively, I recently experimented with using GitHub Issues – requiring just two simple steps to create a dynamically updatable feed page.
Core Concept
Use GitHub Issues as a publishing platform for feed content, then reference those issues on a new static blog page. GitHub Actions monitors issue changes to trigger blog updates automatically.
Advantages:
- Simple setup and maintenance – Built on GitHub. Any issues can be resolved with AI assistance.
- Mobile-friendly – GitHub’s mobile app allows pinning issues to the home screen for quick posting, similar to WeChat Moments.
- Secure and scalable – Fully controlled content with unlimited capacity and easy migration.
Disadvantages: None identified yet.
Implementation Guide
Step 1: Create a Feed Page in Your Blog Template
For Hugo, create a file like moments.html in the layouts folder, using resources.GetRemote to fetch content from a GitHub Issue (e.g., https://api.github.com/repos/microsoft/vscode/issues/519/comments).
Adapt a static page template with your theme settings.
Example:
1{{ define "main" }}
2<article class="main-article">
3 {{ $url := "https://api.github.com/repos/microsoft/vscode/issues/519/comments" }}
4 {{ $opts := dict
5 "headers" (dict "User-Agent" "Hugo Static Site Generator")
6 }}
7 {{ with resources.GetRemote $url $opts }}
8 {{ if and .Content (ne .Content "") }}
9 {{ $comments := .Content | transform.Unmarshal (dict "format" "json") }}
10 <div class="moments-feed">
11 {{ range $comments }}
12 <article class="moment-article">
13 <header class="article-header">
14 <div class="article-details">
15 <footer class="article-time">
16 <div>
17 {{ .created_at | time.Format "2006-01-02 15:04" }}</time>
18 </div>
19 </footer>
20 </div>
21 </header>
22 <section class="article-content">
23 <div class="moment-content">
24 {{ .body | markdownify }}
25 </div>
26 </section>
27 </article>
28 {{ else }}
29 <p>No updates yet</p>
30 {{ end }}
31 </div>
32 {{ else }}
33 <p class="no-content">Empty content</p>
34 {{ end }}
35 {{ else }}
36 <p class="error">⚠️ Failed to load comments</p>
37 {{ end }}
38</article> Step 2: Set Up an Update Trigger
Note: Private repositories block external image access in issues. Use a public repository.
Option A: Blog and Feed in the Same Repo
Add this to your deployment workflow:
1on:
2 issue_comment:
3 types: [created, edited] Option B: Separate Repositories
- Create a public repository dedicated to feed content.
- Add an
issue.ymlfile under.github/workflowsas a trigger. - Generate a
Personal access tokenwithrepopermissions in GitHub settings, then add it to the feed repo’sSecretsasPAT.
1name: Trigger Empty Commit on Issue Update
2
3on:
4 issue_comment:
5 types: [created, edited]
6
7jobs:
8 trigger-empty-commit:
9 runs-on: ubuntu-latest
10 steps:
11 - name: Check if the comment is on the target issue
12 id: check-issue
13 run: |
14 if [ "${{ github.event.issue.number }}" != "1" ]; then
15 echo "should_trigger=false" >> $GITHUB_OUTPUT
16 else
17 echo "should_trigger=true" >> $GITHUB_OUTPUT
18 fi
19
20 - name: Trigger empty commit in user.github.io
21 if: steps.check-issue.outputs.should_trigger == 'true'
22 uses: actions/github-script@v6
23 env:
24 PAT: ${{ secrets.PAT }}
25 with:
26 script: |
27 const { execSync } = require('child_process');
28 const repo = 'user/user.github.io'; // Replace with your Hugo repo
29 const token = process.env.PAT;
30
31 try {
32 const repoUrl = `https://x-access-token:${token}@github.com/${repo}.git`;
33 console.log(`Cloning ${repo}...`);
34
35 execSync(`git clone ${repoUrl}`);
36 process.chdir('user.github.io'); // Replace with your Hugo repo
37
38 execSync('git config user.name "github-actions[bot]"');
39 execSync('git config user.email "4189+github-actions[bot]@users.noreply.github.com"');
40
41 execSync('git commit --allow-empty -m "Trigger update from moments/issues/1"');
42 execSync(`git push ${repoUrl} master`);
43 console.log('✅ Empty commit pushed successfully!');
44 } catch (error) {
45 console.error('❌ Error:', error.message);
46 process.exit(1);
47 }