Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions nuxt/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default defineContentConfig({
date: z.coerce.date(),
authors: z.array(z.string()).optional(),
issues: z.array(z.string()).optional(),
metaTitle: z.string().optional(),
})
}),
// Source files stay at src/blog/ (11ty's historical location) rather than
Expand All @@ -113,6 +114,7 @@ export default defineContentConfig({
schema: z.object({
subtitle: z.string().optional(),
description: z.string().optional(),
metaTitle: z.string().optional(),
date: z.coerce.date(),
lastUpdated: z.coerce.date().optional(),
authors: z.array(z.string()).optional(),
Expand Down
15 changes: 15 additions & 0 deletions nuxt/content/handbook/marketing/content-strategy/blog.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ below more

The title of the page can be seen on both the blog index and the articles.

### Meta Title

`metaTitle` is an optional field that overrides the browser tab title and Open Graph (social share) title — it does not change the on-page `title` shown as the article's H1 or on the blog index.

```yaml
---
title: "Building Digital Work Instructions Dashboard for the Shop Floor"
metaTitle: "Digital Work Instructions Dashboard"
---
```

Use it when the on-page `title` is written for readers (descriptive, sometimes long) but doesn't make a good search-result or tab title. When set, it renders as `{metaTitle} • FlowFuse Blog` in both places; when omitted, the browser tab and share title fall back to `{title} • FlowFuse Blog`.

Keep `metaTitle` itself to 60 characters or fewer (before the ` • FlowFuse Blog` suffix is added) so the full title doesn't get truncated in Google search results, and keyword-forward — it's for SEO/CTR in search results and social previews, not for readability on the page itself.

### Subtitle

The subtitle is only shown on the articles.
Expand Down
46 changes: 46 additions & 0 deletions nuxt/lib/meta-title-length.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Enforces the metaTitle length guidance documented in
// nuxt/content/handbook/marketing/content-strategy/blog.md#meta-title.
// Kept free of Nuxt imports so `node --test` can run it directly.

import { readFileSync, readdirSync, statSync } from 'node:fs'
import { join } from 'node:path'
import yaml from 'js-yaml'

// Google truncates search-result titles at roughly this width; metaTitle is meant to fit
// on its own, before the ` • FlowFuse Blog`/`FlowFuse Changelog` suffix is appended.
export const MAX_META_TITLE_LENGTH = 60

/** Recursively lists every `.md` file under `dir`. */
function listMarkdownFiles (dir) {
return readdirSync(dir, { withFileTypes: true }).flatMap(entry => {
const path = join(dir, entry.name)
if (entry.isDirectory()) return listMarkdownFiles(path)
return entry.isFile() && entry.name.endsWith('.md') ? [path] : []
})
}

/** Parses the YAML frontmatter of a markdown file, or `null` if it has none. */
export function readFrontmatter (filePath) {
const content = readFileSync(filePath, 'utf8')
const match = content.match(/^---\n([\s\S]*?)\n---/)
return match ? yaml.load(match[1]) : null
}

/**
* Every `metaTitle` under `dir` (recursively) that exceeds MAX_META_TITLE_LENGTH characters.
* Files with no `metaTitle` set are skipped - only authors who set the field are held to it.
*/
export function findOverlongMetaTitles (dir) {
return listMarkdownFiles(dir)
.map(file => ({ file, metaTitle: readFrontmatter(file)?.metaTitle }))
.filter(({ metaTitle }) => typeof metaTitle === 'string' && metaTitle.length > MAX_META_TITLE_LENGTH)
.map(({ file, metaTitle }) => ({ file, metaTitle, length: metaTitle.length }))
}

export function isDirectory (path) {
try {
return statSync(path).isDirectory()
} catch {
return false
}
}
32 changes: 32 additions & 0 deletions nuxt/lib/meta-title-length.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'

import { MAX_META_TITLE_LENGTH, findOverlongMetaTitles, isDirectory } from './meta-title-length.mjs'

const repoRoot = join(dirname(fileURLToPath(import.meta.url)), '../..')
const blogDir = join(repoRoot, 'src/blog')
const changelogDir = join(repoRoot, 'src/changelog')

// Guards the fixture-free tests below against a silent no-op if the source layout moves.
test('src/blog and src/changelog exist where these tests expect them', () => {
assert.ok(isDirectory(blogDir), `expected ${blogDir} to exist`)
assert.ok(isDirectory(changelogDir), `expected ${changelogDir} to exist`)
})

test('every blog post metaTitle fits within the handbook-documented length limit', () => {
const violations = findOverlongMetaTitles(blogDir)
assert.deepEqual(violations, [], formatViolations(violations))
})

test('every changelog entry metaTitle fits within the handbook-documented length limit', () => {
const violations = findOverlongMetaTitles(changelogDir)
assert.deepEqual(violations, [], formatViolations(violations))
})

function formatViolations (violations) {
return violations
.map(({ file, metaTitle, length }) => `${file}: "${metaTitle}" is ${length} chars (max ${MAX_META_TITLE_LENGTH})`)
.join('\n')
}
3 changes: 2 additions & 1 deletion nuxt/pages/blog/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ const tldrText = computed(() => typeof page.value?.tldr === 'string' ? page.valu
const pageTitle = computed(() => page.value?.title || 'Blog')
provide('blogPostTitle', pageTitle)
const pageDescription = computed(() => page.value?.description || page.value?.meta?.description || '')
const seoTitle = computed(() => page.value?.metaTitle || pageTitle.value)
const canonicalUrl = computed(() => `https://flowfuse.com${route.path}`)
const absoluteImage = computed(() => heroImage.value.startsWith('http') ? heroImage.value : `https://flowfuse.com${heroImage.value}`)

Expand All @@ -110,7 +111,7 @@ useHead({
}, { tagPriority: 1000 })

useSeoMeta({
title: pageTitle,
title: seoTitle,
description: computed(() => routeInfo.value.kind === 'post' ? pageDescription.value : ''),
ogDescription: computed(() => routeInfo.value.kind === 'post' ? pageDescription.value : ''),
ogImage: computed(() => routeInfo.value.kind === 'post' ? absoluteImage.value : undefined),
Expand Down
3 changes: 2 additions & 1 deletion nuxt/pages/changelog/[...slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function issueLabel(issue: string): string {
}

const pageTitle = computed(() => page.value?.title || 'Changelog')
const seoTitle = computed(() => page.value?.metaTitle || pageTitle.value)
const canonicalUrl = computed(() => `https://flowfuse.com${route.path}`)

// Changelog entries always get the "Changelog" qualifier on the brand name.
Expand All @@ -48,7 +49,7 @@ const canonicalUrl = computed(() => `https://flowfuse.com${route.path}`)
useHead({ templateParams: { siteName: 'FlowFuse Changelog' } }, { tagPriority: 1000 })

useSeoMeta({
title: pageTitle,
title: seoTitle,
description: computed(() => page.value?.description || ''),
ogDescription: computed(() => page.value?.description || ''),
ogUrl: canonicalUrl,
Expand Down
8 changes: 6 additions & 2 deletions src/_includes/layouts/base.njk
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ eleventyComputed:
{% endif %}

<!-- Browser Title -->
{% if navTitle %}
{% if metaTitle %}
Comment thread
ZJvandeWeg marked this conversation as resolved.
<title>{{ metaTitle }} | FlowFuse</title>
{% elif navTitle %}
<title>{{ navTitle }} &#x2022; FlowFuse{% if page.url and page.url.match('\/handbook\/.+') %} Handbook{% endif %}{% if page.url and page.url.match('\/docs\/.+') %} Docs{% endif %}</title>
{% elif meta and meta.title %}
<title>{{ meta.title }} &#x2022; FlowFuse{% if page.url and page.url.match('\/handbook\/.+') %} Handbook{% endif %}{% if page.url and page.url.match('\/docs\/.+') %} Docs{% endif %}</title>
Expand Down Expand Up @@ -84,7 +86,9 @@ eleventyComputed:
{%- endif %}

<!-- Open Graph Title -->
{% if navTitle %}
{% if metaTitle %}
<meta property="og:title" content="{{ metaTitle }} | FlowFuse" />
{% elif navTitle %}
<meta property="og:title" content="{{ navTitle }} &#x2022; FlowFuse{% if page.url and page.url.match('\/handbook\/.+') %} Handbook{% endif %}" />
{% elif meta and meta.title %}
<meta property="og:title" content="{{ meta.title }} &#x2022; FlowFuse{% if page.url and page.url.match('\/handbook\/.+') %} Handbook{% endif %}" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse Raises $7.25M Seed Round"
title: FlowFuse raises $7.25M Seed Round to bring Node-RED to the Enterprise
subtitle: Allowing all developers to integrate IT and OT through low-code
description: "FlowFuse raises a $7.25M Seed Round from Cota Capital and Open Core Ventures to bring Node-RED to the enterprise market."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2022/12/flowforge-1-2-0-released.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse 1.2: Single Sign On & Context Storage"
title: FlowFuse 1.2 is now available with single sign on and persistent context storage
subtitle: Our final release for 2022 with some great new features to try out
description: "FlowFuse 1.2 is now available with single sign-on and persistent context storage, marking the final release for 2022 overall."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/01/flowforge-1-3-0-released.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse 1.3: Share Flows via Team Libraries"
title: FlowFuse 1.3 is now available, share your flows through our new team libraries and much more
subtitle: Our first release of 2023 with some great new features to try out, happy new year from everyone at FlowFuse!
description: "FlowFuse 1.3 is now available. Share your flows through the brand new team libraries, plus much more in this latest release."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/02/flowforge-1-4-0-released.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse v1.4: Bulk Device Provisioning"
title: FlowFuse v1.4 with device provisioning in bulk and staged development process
subtitle: Our second release of 2023 with some great new features to try out.
description: Deploy Node-RED to many devices quickly, and allow a staged development process with the latest release of FlowFuse v1.4.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Node-RED: Integration Platform for IIoT Edge"
title: 'Node-RED: The Integration Platform for IIoT Edge Computing & PLCs'
subtitle: Node-RED's Role in IIoT Edge Computing & PLC Integration
description: Discover why Node-RED is the go-to integration platform for IIoT edge computing and PLCs, embraced by leading vendors for its versatility and ease of use.
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/05/device-agent-as-a-service.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Running the Device Agent on a Raspberry Pi"
title: Running the FlowFuse Device Agent as a service on a Raspberry Pi
subtitle: Step by step guide to run the device agent as a service
description: "Learn how to run the FlowFuse Device Agent as a service on your Raspberry Pi, ensuring uninterrupted operation after restarts."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/05/flowforge-1-7-released.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse 1.7: Remote Node-RED Editor Access"
title: FlowFuse 1.7 Now Available with Remote Node-RED Editor Access
subtitle: Further improving fleet management and maintenance of remote Node-RED instances
description: "FlowFuse 1.7 is now available with remote Node-RED Editor access, letting teams edit flows directly on their own devices."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/05/integrating-modbus-with-node-red.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Integrating a Modbus Device With Node-RED"
title: "Best Practices Integrating a Modbus Device With Node-RED (2026)"
subtitle: Integrate Modbus with Node-RED
description: "Modbus is a widely adopted protocol for legacy manufacturing equipment. Learn best practices for integrating it with Node-RED."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/06/dashboard-announcement.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "The Successor to the Node-RED Dashboard"
title: The Next Step in Data Visualization - Announcing the Successor to the Node-RED Dashboard
subtitle: FlowFuse's Journey Towards a New Node-RED Dashboard
description: "FlowFuse unveils its plans to build the successor to the Node-RED Dashboard, the next step in industrial data visualization."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "How to Build an OPC UA Client Dashboard - Part 3"
title: "How to Build an OPC UA Client Dashboard in Node-RED - Part 3 (2026)"
subtitle: Interactive OPC UA Client dashboard that communicates with a 3rd party OPC UA Server
description: Building a Dashboard-Driven OPC UA Client to Browse, Read, Write, and Get Events from a 3rd party OPC UA Server
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Deploy a Basic OPC-UA Server in Node-RED - Part 1"
title: "How to Deploy a Basic OPC-UA Server in Node-RED - Part 1 (2026)"
subtitle: OPC-UA Server Information Modeling in Node-RED
description: "An introduction to OPC-UA and how to deploy a Node-RED server flow, Part 1 of a series on building OPC-UA servers with FlowFuse."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/07/images-in-node-red-dashboards.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Add Images to Node-RED Dashboards"
title: "How to add images to Node-RED dashboards when using FlowFuse (2026)"
subtitle: Import your images into your Node-RED dashboards, wherever you are running your instances
description: Learn to enhance Node-RED dashboards with images using FlowFuse. Pull images from URLs, store locally, and serve them in your dashboards.
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/07/influxdb-historical-data.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Historical Data Dashboard with InfluxDB"
title: Creating a Historical Data Dashboard with InfluxDB and Node-RED
subtitle: Detailed instructions on how to create a Node-RED dashboard that shows historical data.
description: Discover how to build a Historical Data Dashboard with InfluxDB and Node-RED. Capture, store, and visualize data for insightful analysis.
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/08/flowfuse-1-11-release.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse 1.11: Easier Node-RED Setup"
title: FlowFuse 1.11 makes it easier to get started with FlowFuse and Node-RED
subtitle: Our latest release includes a new starter tier for FlowFuse Cloud, Personal Access Tokens for API access and improvements to device management.
description: The new FlowFuse 1.11 release includes a new starter tier for FlowFuse Cloud, Personal Access Tokens for API access and improvements to device management.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Why the Automation Pyramid Blocks Transformation"
title: Why the Automation Pyramid blocks digital transformation - The Role of Unified Namespace
subtitle: A Critical Examination of the Automation Pyramid's Obstruction to Digital Transformation
description: "This article analyzes the Automation Pyramid's constraints and explains how Unified Namespace can evolve digital transformation."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/09/bosch-rexroth-announce.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse Node-RED Stack for ctrlX AUTOMATION"
title: FlowFuse announces a Node-RED stack for Industry 4.0 applications on ctrlX AUTOMATION
subtitle: Rexroth ctrlX now have fully supported Node-RED stack available for production use
description: FlowFuse is now offering Node-RED to customers that want to deploy it on the Rexrtoh ctrlX platform.
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/10/citizen-development.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Why Manufacturing Must Embrace Citizen Developers"
title: Innovate from within - Why manufacturing must embrace Citizen Developers
subtitle: Empower your Operational Technology teams as Citizen Developers
description: "Explore the significance of Citizen Developers in manufacturing, bridging the IT-OT gap with low-code platforms like Node-RED."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/12/ai-use-cases.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Beyond Automation: AI Use Cases in Manufacturing"
title: Beyond Automation - AI Use Cases that are shaping the next manufacturing frontier
subtitle: In which AI-powered capabilities should one invest to bring about transformative changes in the manufacturing environment?
description: Discover how AI is revolutionizing manufacturing with citizen development, demand forecasting, and predictive maintenance
Expand Down
1 change: 1 addition & 0 deletions src/blog/2023/12/introduction-to-unified-namespace.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Introduction to the Unified Namespace (UNS)"
title: "Introduction to the Unified Namespace (UNS) – 2026 Updated Guide"
subtitle: "Making data available for Industry 4.0 use-cases"
description: Explore how the Unified Namespace (UNS) empowers Industry 4.0 with seamless data exchange, maximizing organizational potential.
Expand Down
1 change: 1 addition & 0 deletions src/blog/2024/01/dashboard-2-multi-user.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Personalised Multi-user Dashboards with Node-RED"
title: Personalised Multi-user Dashboards with Node-RED Dashboard 2.0!
subtitle: Explore how to build multi-user Dashboards, secured with FlowFuse Cloud!
description: "Discover how to create personalized, secured multi-user dashboards with FlowFuse Cloud and the Dashboard 2.0 User Addon."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Deploying Node-RED with FlowFuse in balenaCloud"
title: Step-by-Step Guide to Deploying Node-RED with FlowFuse in balenaCloud
subtitle: Fleet management made easier with FlowFuse and balena.
description: Deploy Node-RED with FlowFuse on balenaCloud effortlessly with our step-by-step guide. Simplify fleet management and enhance data processing capabilities.
Expand Down
1 change: 1 addition & 0 deletions src/blog/2024/01/unified-namespace-when-not-to-use.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Unified Namespace: When to Use It"
title: "Unified Namespace: When to Use It, and When to Choose Something Else"
subtitle: Data isn't created equal, some data doesn't fit the UNS
description: "Explore when to use the Unified Namespace (UNS) architecture and when to choose alternatives, covering latency and security."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2024/02/professional-services-for-node-red.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Invest in Node-RED Professional Services?"
title: Should You Invest in Professional Services for Your Node-RED Development?
subtitle: Professional Services for Node-RED, When and Why?
description: "Discover the benefits of investing in professional services for your Node-RED development, from initial setup to full scaling."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse and Gallarus Strategic Partnership"
title: FlowFuse and Gallarus Announce Strategic Partnership to Accelerate Industry 4.0 Adoption
subtitle: Strategic partnership to empower businesses with low-code development for Industry 4.0 Transformation
description: "FlowFuse and Gallarus announce a strategic partnership to accelerate Industry 4.0 adoption across manufacturing operations."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse Instance vs Device Instance"
title: "Scaling Node-RED with FlowFuse: Differences between a FlowFuse Instance and a Device Instance"
subtitle: Managing your Node-RED instances is easier with FlowFuse.
description: "With FlowFuse, Node-RED instances can be scaled and managed easily. Learn the difference between an Instance and a Device."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "How to Build an Admin Dashboard with Node-RED"
title: "How to Build an Admin Dashboard with Node-RED Dashboard 2.0 (2026)"
subtitle: A guide to building an Admin Dashboard in Node-RED with Dashboard 2.0
description: "Discover step-by-step instructions for building an admin-only page in Node-RED Dashboard 2.0 using the FlowFuse Multiuser addon."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Build An Application With Node-RED Dashboard 2.0"
title: "How to Build An Application With Node-RED Dashboard 2.0 (2026)"
subtitle: A step-by-step guide to building a personalized, secure, and fully functional application with Dashboard 2.0.
description: "Learn to build custom applications effortlessly with Node-RED Dashboard 2.0 in this step-by-step, secure application guide."
Expand Down
1 change: 1 addition & 0 deletions src/blog/2024/05/flowfuse-2-4-release.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "FlowFuse 2.4: Easier Snapshots & Blueprints"
title: "FlowFuse 2.4: making it easier to work with Snapshots, Blueprints & Devices"
subtitle: Our latest release introduces better ways to work with Snapshots, Blueprints, view the content of you flows in FlowFuse, and manage the version of Node-RED running on Devices
description: "FlowFuse 2.4 introduces better ways to work with Snapshots and Blueprints, and manage the Node-RED version running on Devices."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Node-RED Dashboard 2.0 Layout & Styling Guide"
title: "Comprehensive guide: Node-RED Dashboard 2.0 layout, sidebar, and styling"
subtitle: Explore Dashboard 2.0 Different layouts and sidebars. learn how to style Dashboard 2.0 elements effortlessly.
description: "Discover Node-RED Dashboard 2.0's three layouts, five sidebar styles, themes, and custom CSS in this complete styling guide."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
metaTitle: "Node-RED Variables: Flow, Global & Context"
title: "How to Use Variables in Node-RED: Flow, Global, Context & Environment (2026)"
subtitle: A complete guide to setting, retrieving, and persisting Node-RED variables for efficient, production-ready flows.
description: "Learn how to use Node-RED global, flow, context, and environment variables in 2026, with step-by-step examples and an FAQ."
Expand Down
Loading
Loading