How to Use Notion Flow Blocks for Conversion

✍🏼 Written on Mar 31, 2024    💡 Updated on Apr 1, 2024
❗️ Note: it has been days since this article was written, please be aware of its timeliness
🖥  Note:I Have Unique Notion Flow Usage Tips~

A week ago, I built the Notion Flow browser extension:

Xheldon on Twitter / X The Notion Flow browser extension is finally released! For setup and introduction, check out the video: https://t.co/pw4yYwnt8h官网见:https://t.co/6326Q4gIWC插件用来将 Notion content can be sent to Github in Markdown or any custom format you define. I use it to write my Jekyll blog hosted on Github Pages. The extension automatically handles image content to CDN after OSS configuration—super handy~. — Xheldon (@\_Xheldon) March 20, 2024 https://twitter.com/_Xheldon/status/1770466495560294583

And now, the freshly updated 0.4.1 version:

0.4.1 | Notion Flow Feature https://notion-flow.xheldon.com/blog/2024/03/31/0.4.1

Supports self-hosted OSS services compatible with the AWS S3 API, such as Cloudflare R2:

Cloudflare R2 | 零出口费用分布式对象存储 | Cloudflare | Cloudflare Cloudflare R2 is an S3-compatible, zero egress fee, globally distributed object storage. Freely move data and build your desired multi-cloud architecture. https://www.cloudflare.com/zh-cn/developer-platform/r2/

This post briefly explains how I use this browser extension for my Github Jekyll blog.


Jekyll static blogs are built on Ruby and support plugins. So I wrote a few plugins myself (Jekyll blog plugins are located in the _plugins directory—just drop the Ruby file there and restart the service) to handle Liquid templating, with content sourced from Notion via Notion Flow conversion. For example, here’s the plugin code for processing bookmarks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
module Jekyll
class RenderBookMarkBlock < Liquid::Block
def initialize(tag_name, attr, tokens)
super
# 普通的链接没有 yid 和 bid
attrs = attr.scan(/url\=\"(.*)\"\stitle\=\"(.*)\"\simg\=\"(.*)\"\syid\=\"(.*)\"\sbid\=\"(.*)\"/)
if !attrs.empty?
# 外部的 video 链接,youtube、bilibili(如本文上一篇博客就是)
@url = attrs[0][0]
@title = attrs[0][1]
@img = attrs[0][2]
@yid = attrs[0][3]
@bid = attrs[0][4]
@firstChar = (@title)[0].upcase
@error = ""
else
# 正常和 notion 一样的 bookmark(如本文上面三个链接就是)
attrs = attr.scan(/url\=\"(.*)\"\stitle\=\"(.*)\"\simg\=\"(.*)\"/)
@url = attrs[0][0]
@title = attrs[0][1]
@img = attrs[0][2]
@firstChar = (@title)[0].upcase
@error = ""
end
end
def render(context)
@desc = super
if !@yid.nil? && !@yid.empty?
"<p class='embed-responsive embed-responsive-16by9'><iframe src='https://www.youtube.com/embed/#{@yid}?rel=0' title='YouTube video player' frameborder='0' allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture' allowfullscreen></iframe></p>"
elsif !@bid.nil? && !@bid.empty?
"<p class='embed-responsive embed-responsive-16by9' style='border-bottom: 1px solid #ddd;'><iframe src='//player.bilibili.com/player.html?bvid=#{@bid}&high_quality=1&as_wide=1' scrolling='no' border='0' frameborder='no' framespacing='0' allowfullscreen></iframe></p>"
else
"<p><a class='link-bookmark' href='#{@url}' target='_blank'><span data-bookmark-img='#{@img}' data-bookmark-title='#{@firstChar}'><img src='#{@img}'/></span><span><span>#{@title}</span><span>#{@desc}</span><span>#{@url}</span></span></a></p>"
end
end
end
end

# 上传的 video
module Jekyll
class RenderVideoBlock < Liquid::Block
def initialize(tag_name, attr, tokens)
super
attrs = attr.scan(/caption\=\"(.*)\"\simg\=\"(.*)\"\ssuffix\=\"(.*)\"/)
@caption = attrs[0][0]
@img = attrs[0][1]
@suffix = attrs[0][2]
end
def render(context)
text = super
"<p caption='#{@caption}'><video controls muted><source src='#{@img}' type='video/#{@suffix}' /></video></p>"
end
end
end

Liquid::Template.register_tag('render_bookmark', Jekyll::RenderBookMarkBlock)
Liquid::Template.register_tag('render_video', Jekyll::RenderVideoBlock)

The logic here is: if a Notion bookmark module link points to YouTube or Bilibili, it’s converted to embedded video HTML (iframe); otherwise, it’s transformed into HTML resembling Notion bookmarks (requiring CSS for styling).

Thus, while using Notion Flow to convert Notion content into Markdown format, I customized the conversion rules for modules like bookmarks to ensure the blog displays YouTube, Bilibili, and Notion-style bookmark content, as shown here:

1
2
3
4
5
6
7
8
9
10
11
12
13
video: function video(block) {
if (block.type === 'file') {
// 用户自己上传的 video 文件,用默认 video 插件处理
return `{% render_video caption="${block.caption}" img="${block.url}" suffix="${block.suffix}" %}\n![${block.caption}](${block.url})\n{% endrender_video %}\n`;
} else if (block.type === 'external') {
// 外部链接、youtube 和 bilibili 视频链接,用 bookmark 处理
return `{% render_bookmark url="${block.url}" title="${
block.caption || ''
}" img="" yid="${block.yid}" bid="${
block.bid
}" %}{% endrender_bookmark %}\n`;
}
}

One thing to note (I’m not deeply familiar with Ruby): Liquid template tags must enclose text content (even if unused), or the Ruby plugin won’t generate HTML. For example:

1
{% render_video  %}这里必须有任意内容!{% endrender_video %}

In the Ruby plugin, the super variable will receive the phrase “There must be some content here!” (you may choose not to use this variable). If this content is absent, the plugin will not return anything at all.

- EOF -
Originally published at: How to Use Notion Flow Blocks for Conversion - Xheldon Blog