This is an automated email from the ASF dual-hosted git repository.

jli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 46bca32677c docs(seo): add structured data, OpenGraph tags, and 
sitemap improvements (#37404)
46bca32677c is described below

commit 46bca32677cdff0247e3494870efdb9971f04c3b
Author: Evan Rusackas <[email protected]>
AuthorDate: Fri Feb 6 14:09:19 2026 -0500

    docs(seo): add structured data, OpenGraph tags, and sitemap improvements 
(#37404)
    
    Co-authored-by: Claude Opus 4.5 <[email protected]>
---
 docs/docs/faq.mdx                           |  56 ++++++++++
 docs/docusaurus.config.ts                   | 143 +++++++++++++++++++++++++-
 docs/plugins/remark-tech-article-schema.mjs | 153 ++++++++++++++++++++++++++++
 docs/plugins/robots-txt-plugin.js           |  83 +++++++++++++++
 docs/src/components/FAQSchema.tsx           |  66 ++++++++++++
 docs/src/components/TechArticleSchema.tsx   |  91 +++++++++++++++++
 docs/static/img/superset-og-image.png       | Bin 0 -> 89831 bytes
 7 files changed, 589 insertions(+), 3 deletions(-)

diff --git a/docs/docs/faq.mdx b/docs/docs/faq.mdx
index 154d668b4e6..d38c39eadad 100644
--- a/docs/docs/faq.mdx
+++ b/docs/docs/faq.mdx
@@ -1,7 +1,63 @@
 ---
 sidebar_position: 9
+title: Frequently Asked Questions
+description: Common questions about Apache Superset including performance, 
database support, visualizations, and configuration.
+keywords: [superset faq, superset questions, superset help, data visualization 
faq]
 ---
 
+import FAQSchema from '@site/src/components/FAQSchema';
+
+<FAQSchema faqs={[
+  {
+    question: "How big of a dataset can Superset handle?",
+    answer: "Superset can work with even gigantic databases. Superset acts as 
a thin layer above your underlying databases or data engines, which do all the 
processing. Superset simply visualizes the results of the query. The key to 
achieving acceptable performance is whether your database can execute queries 
and return results at acceptable speed."
+  },
+  {
+    question: "What are the computing specifications required to run 
Superset?",
+    answer: "The specs depend on how many users you have and their activity, 
not on the size of your data. Community members have reported 8GB RAM, 2vCPUs 
as adequate for a moderately-sized instance. Monitor your resource usage and 
adjust as needed."
+  },
+  {
+    question: "Can I join or query multiple tables at one time?",
+    answer: "Not in the Explore or Visualization UI directly. A Superset 
SQLAlchemy datasource can only be a single table or a view. You can create a 
view that joins tables, or use SQL Lab where you can write SQL queries to join 
multiple tables."
+  },
+  {
+    question: "How do I create my own visualization?",
+    answer: "Read the instructions in the Creating Visualization Plugins 
documentation to learn how to build custom visualizations for Superset."
+  },
+  {
+    question: "Can I upload and visualize CSV data?",
+    answer: "Yes! Superset supports CSV upload functionality. Read the 
Exploring Data documentation to learn how to enable and use CSV upload."
+  },
+  {
+    question: "Why are my queries timing out?",
+    answer: "There are many possible causes. For SQL Lab, Superset allows 
queries to run up to 6 hours by default (configurable via 
SQLLAB_ASYNC_TIME_LIMIT_SEC). For dashboard timeouts, check your gateway/proxy 
timeout settings and adjust SUPERSET_WEBSERVER_TIMEOUT in superset_config.py."
+  },
+  {
+    question: "Why is the map not visible in the geospatial visualization?",
+    answer: "You need to register a free account at Mapbox.com, obtain an API 
key, and add it to your .env file at the key MAPBOX_API_KEY."
+  },
+  {
+    question: "What database engine can I use as a backend for Superset?",
+    answer: "Superset is tested using MySQL, PostgreSQL, and SQLite backends 
for storing its internal metadata. While Superset supports many databases as 
data sources, only these are recommended for the metadata store in production."
+  },
+  {
+    question: "Does Superset work with my database?",
+    answer: "Superset supports any database with a Python SQLAlchemy dialect 
and DBAPI driver. Check the Connecting to Databases documentation for the full 
list of supported databases."
+  },
+  {
+    question: "Does Superset offer a public API?",
+    answer: "Yes, Superset has a public REST API documented using Swagger. 
Enable FAB_API_SWAGGER_UI in superset_config.py to access interactive API 
documentation at /swagger/v1."
+  },
+  {
+    question: "Does Superset collect any telemetry data?",
+    answer: "Superset uses Scarf by default to collect basic telemetry data to 
help maintainers understand version usage. Users can opt out by setting the 
SCARF_ANALYTICS environment variable to false."
+  },
+  {
+    question: "Does Superset have a trash bin to recover deleted assets?",
+    answer: "No, there is no built-in way to recover deleted dashboards, 
charts, or datasets. It is recommended to take periodic backups of the metadata 
database and use export functionality for recovery."
+  }
+]} />
+
 # FAQ
 
 ## How big of a dataset can Superset handle?
diff --git a/docs/docusaurus.config.ts b/docs/docusaurus.config.ts
index 8cab4b41d86..f22a1ce846c 100644
--- a/docs/docusaurus.config.ts
+++ b/docs/docusaurus.config.ts
@@ -23,6 +23,7 @@ import type * as OpenApiPlugin from 
'docusaurus-plugin-openapi-docs';
 import { themes } from 'prism-react-renderer';
 import remarkImportPartial from 'remark-import-partial';
 import remarkLocalizeBadges from './plugins/remark-localize-badges.mjs';
+import remarkTechArticleSchema from './plugins/remark-tech-article-schema.mjs';
 import * as fs from 'fs';
 import * as path from 'path';
 
@@ -46,7 +47,7 @@ if (!versionsConfig.components.disabled) {
       sidebarPath: require.resolve('./sidebarComponents.js'),
       editUrl:
         'https://github.com/apache/superset/edit/master/docs/components',
-      remarkPlugins: [remarkImportPartial, remarkLocalizeBadges],
+      remarkPlugins: [remarkImportPartial, remarkLocalizeBadges, 
remarkTechArticleSchema],
       admonitions: {
         keywords: ['note', 'tip', 'info', 'warning', 'danger', 'resources'],
         extendDefaults: true,
@@ -74,7 +75,7 @@ if (!versionsConfig.developer_portal.disabled) {
       sidebarPath: require.resolve('./sidebarTutorials.js'),
       editUrl:
         'https://github.com/apache/superset/edit/master/docs/developer_portal',
-      remarkPlugins: [remarkImportPartial, remarkLocalizeBadges],
+      remarkPlugins: [remarkImportPartial, remarkLocalizeBadges, 
remarkTechArticleSchema],
       admonitions: {
         keywords: ['note', 'tip', 'info', 'warning', 'danger', 'resources'],
         extendDefaults: true,
@@ -180,6 +181,83 @@ const config: Config = {
   favicon: '/img/favicon.ico',
   organizationName: 'apache',
   projectName: 'superset',
+
+  // SEO: Structured data (Organization, Software, WebSite with SearchAction)
+  headTags: [
+    // SoftwareApplication schema
+    {
+      tagName: 'script',
+      attributes: {
+        type: 'application/ld+json',
+      },
+      innerHTML: JSON.stringify({
+        '@context': 'https://schema.org',
+        '@type': 'SoftwareApplication',
+        name: 'Apache Superset',
+        applicationCategory: 'BusinessApplication',
+        operatingSystem: 'Cross-platform',
+        description: 'Apache Superset is a modern, enterprise-ready business 
intelligence web application for data exploration and visualization.',
+        url: 'https://superset.apache.org',
+        license: 'https://www.apache.org/licenses/LICENSE-2.0',
+        author: {
+          '@type': 'Organization',
+          name: 'Apache Software Foundation',
+          url: 'https://www.apache.org/',
+          logo: 'https://www.apache.org/foundation/press/kit/asf_logo.png',
+        },
+        offers: {
+          '@type': 'Offer',
+          price: '0',
+          priceCurrency: 'USD',
+        },
+        featureList: [
+          'Interactive dashboards',
+          'SQL IDE',
+          '40+ visualization types',
+          'Semantic layer',
+          'Role-based access control',
+          'REST API',
+        ],
+      }),
+    },
+    // WebSite schema with SearchAction (enables sitelinks search box in 
Google)
+    {
+      tagName: 'script',
+      attributes: {
+        type: 'application/ld+json',
+      },
+      innerHTML: JSON.stringify({
+        '@context': 'https://schema.org',
+        '@type': 'WebSite',
+        name: 'Apache Superset',
+        url: 'https://superset.apache.org',
+        potentialAction: {
+          '@type': 'SearchAction',
+          target: {
+            '@type': 'EntryPoint',
+            urlTemplate: 
'https://superset.apache.org/search?q={search_term_string}',
+          },
+          'query-input': 'required name=search_term_string',
+        },
+      }),
+    },
+    // Preconnect hints for faster external resource loading
+    {
+      tagName: 'link',
+      attributes: {
+        rel: 'preconnect',
+        href: 'https://WR5FASX5ED-dsn.algolia.net',
+        crossorigin: 'anonymous',
+      },
+    },
+    {
+      tagName: 'link',
+      attributes: {
+        rel: 'preconnect',
+        href: 'https://analytics.apache.org',
+      },
+    },
+  ],
   themes: [
     '@saucelabs/theme-github-codeblock',
     '@docusaurus/theme-mermaid',
@@ -212,6 +290,19 @@ const config: Config = {
         },
       },
     ],
+    // SEO: Generate robots.txt during build
+    [
+      require.resolve('./plugins/robots-txt-plugin.js'),
+      {
+        policies: [
+          {
+            userAgent: '*',
+            allow: '/',
+            disallow: ['/api/v1/', '/_next/', '/static/js/*.map'],
+          },
+        ],
+      },
+    ],
     [
       '@docusaurus/plugin-client-redirects',
       {
@@ -373,7 +464,7 @@ const config: Config = {
             }
             return 
`https://github.com/apache/superset/edit/master/docs/${versionDocsDirPath}/${docPath}`;
           },
-          remarkPlugins: [remarkImportPartial, remarkLocalizeBadges],
+          remarkPlugins: [remarkImportPartial, remarkLocalizeBadges, 
remarkTechArticleSchema],
           admonitions: {
             keywords: ['note', 'tip', 'info', 'warning', 'danger', 
'resources'],
             extendDefaults: true,
@@ -396,11 +487,57 @@ const config: Config = {
         theme: {
           customCss: require.resolve('./src/styles/custom.css'),
         },
+        // SEO: Sitemap configuration with priorities
+        sitemap: {
+          lastmod: 'date',
+          changefreq: 'weekly',
+          priority: 0.5,
+          ignorePatterns: ['/tags/**'],
+          filename: 'sitemap.xml',
+          createSitemapItems: async (params) => {
+            const { defaultCreateSitemapItems, ...rest } = params;
+            const items = await defaultCreateSitemapItems(rest);
+            return items.map((item) => {
+              // Boost priority for key pages
+              if (item.url.includes('/docs/intro')) {
+                return { ...item, priority: 1.0, changefreq: 'daily' };
+              }
+              if (item.url.includes('/docs/quickstart')) {
+                return { ...item, priority: 0.9, changefreq: 'weekly' };
+              }
+              if (item.url.includes('/docs/installation/')) {
+                return { ...item, priority: 0.8, changefreq: 'weekly' };
+              }
+              if (item.url.includes('/docs/databases')) {
+                return { ...item, priority: 0.8, changefreq: 'weekly' };
+              }
+              if (item.url.includes('/docs/faq')) {
+                return { ...item, priority: 0.7, changefreq: 'monthly' };
+              }
+              if (item.url === 'https://superset.apache.org/') {
+                return { ...item, priority: 1.0, changefreq: 'daily' };
+              }
+              return item;
+            });
+          },
+        },
       } satisfies Options,
     ],
   ],
 
   themeConfig: {
+    // SEO: OpenGraph and Twitter meta tags
+    metadata: [
+      { name: 'keywords', content: 'data visualization, business intelligence, 
BI, dashboards, SQL, analytics, open source, Apache, charts, reporting' },
+      { property: 'og:type', content: 'website' },
+      { property: 'og:site_name', content: 'Apache Superset' },
+      { property: 'og:image', content: 
'https://superset.apache.org/img/superset-og-image.png' },
+      { property: 'og:image:width', content: '1200' },
+      { property: 'og:image:height', content: '630' },
+      { name: 'twitter:card', content: 'summary_large_image' },
+      { name: 'twitter:image', content: 
'https://superset.apache.org/img/superset-og-image.png' },
+      { name: 'twitter:site', content: '@ApacheSuperset' },
+    ],
     colorMode: {
       defaultMode: 'dark',
       disableSwitch: false,
diff --git a/docs/plugins/remark-tech-article-schema.mjs 
b/docs/plugins/remark-tech-article-schema.mjs
new file mode 100644
index 00000000000..44c505ac3fb
--- /dev/null
+++ b/docs/plugins/remark-tech-article-schema.mjs
@@ -0,0 +1,153 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// Note: visit from unist-util-visit is available if needed for tree traversal
+
+/**
+ * Remark plugin that automatically injects TechArticle schema import and 
component
+ * into documentation MDX files based on frontmatter.
+ *
+ * This enables rich snippets for technical documentation in search results.
+ *
+ * Frontmatter options:
+ * - title: (required) Article headline
+ * - description: (required) Article description
+ * - keywords: (optional) Array of keywords
+ * - seo_proficiency: (optional) 'Beginner' or 'Expert', defaults to 'Beginner'
+ * - seo_schema: (optional) Set to false to disable schema injection
+ */
+export default function remarkTechArticleSchema() {
+  return (tree, file) => {
+    const frontmatter = file.data.frontMatter || {};
+
+    // Skip if explicitly disabled or missing required fields
+    if (frontmatter.seo_schema === false) {
+      return;
+    }
+
+    // Only add schema if we have title and description
+    if (!frontmatter.title || !frontmatter.description) {
+      return;
+    }
+
+    const title = frontmatter.title;
+    const description = frontmatter.description;
+    const keywords = Array.isArray(frontmatter.keywords) ? 
frontmatter.keywords : [];
+    const proficiencyLevel = frontmatter.seo_proficiency || 'Beginner';
+
+    // Create the import statement
+    const importNode = {
+      type: 'mdxjsEsm',
+      value: `import TechArticleSchema from 
'@site/src/components/TechArticleSchema';`,
+      data: {
+        estree: {
+          type: 'Program',
+          sourceType: 'module',
+          body: [
+            {
+              type: 'ImportDeclaration',
+              specifiers: [
+                {
+                  type: 'ImportDefaultSpecifier',
+                  local: { type: 'Identifier', name: 'TechArticleSchema' },
+                },
+              ],
+              source: {
+                type: 'Literal',
+                value: '@site/src/components/TechArticleSchema',
+              },
+            },
+          ],
+        },
+      },
+    };
+
+    // Create the component node for MDX
+    const componentNode = {
+      type: 'mdxJsxFlowElement',
+      name: 'TechArticleSchema',
+      attributes: [
+        {
+          type: 'mdxJsxAttribute',
+          name: 'title',
+          value: title,
+        },
+        {
+          type: 'mdxJsxAttribute',
+          name: 'description',
+          value: description,
+        },
+        ...(keywords.length > 0
+          ? [
+              {
+                type: 'mdxJsxAttribute',
+                name: 'keywords',
+                value: {
+                  type: 'mdxJsxAttributeValueExpression',
+                  value: JSON.stringify(keywords),
+                  data: {
+                    estree: {
+                      type: 'Program',
+                      sourceType: 'module',
+                      body: [
+                        {
+                          type: 'ExpressionStatement',
+                          expression: {
+                            type: 'ArrayExpression',
+                            elements: keywords.map((k) => ({
+                              type: 'Literal',
+                              value: k,
+                            })),
+                          },
+                        },
+                      ],
+                    },
+                  },
+                },
+              },
+            ]
+          : []),
+        ...(proficiencyLevel !== 'Beginner'
+          ? [
+              {
+                type: 'mdxJsxAttribute',
+                name: 'proficiencyLevel',
+                value: proficiencyLevel,
+              },
+            ]
+          : []),
+      ],
+      children: [],
+    };
+
+    // Insert import at the beginning
+    tree.children.unshift(importNode);
+
+    // Find the first heading and insert component after it
+    let insertIndex = 1; // Default: after import
+    for (let i = 1; i < tree.children.length; i++) {
+      if (tree.children[i].type === 'heading') {
+        insertIndex = i + 1;
+        break;
+      }
+    }
+
+    tree.children.splice(insertIndex, 0, componentNode);
+  };
+}
diff --git a/docs/plugins/robots-txt-plugin.js 
b/docs/plugins/robots-txt-plugin.js
new file mode 100644
index 00000000000..0b9bf348a12
--- /dev/null
+++ b/docs/plugins/robots-txt-plugin.js
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* eslint-disable @typescript-eslint/no-require-imports */
+const fs = require('fs');
+const path = require('path');
+/* eslint-enable @typescript-eslint/no-require-imports */
+
+/**
+ * Docusaurus plugin to generate robots.txt during build
+ * Configuration is passed via plugin options
+ */
+module.exports = function robotsTxtPlugin(context, options = {}) {
+  const { siteConfig } = context;
+  const {
+    policies = [{ userAgent: '*', allow: '/' }],
+    additionalSitemaps = [],
+  } = options;
+
+  return {
+    name: 'robots-txt-plugin',
+
+    async postBuild({ outDir }) {
+      const sitemapUrl = `${siteConfig.url}/sitemap.xml`;
+
+      // Build robots.txt content
+      const lines = [];
+
+      // Add policies
+      for (const policy of policies) {
+        lines.push(`User-agent: ${policy.userAgent}`);
+
+        if (policy.allow) {
+          const allows = Array.isArray(policy.allow) ? policy.allow : 
[policy.allow];
+          for (const allow of allows) {
+            lines.push(`Allow: ${allow}`);
+          }
+        }
+
+        if (policy.disallow) {
+          const disallows = Array.isArray(policy.disallow) ? policy.disallow : 
[policy.disallow];
+          for (const disallow of disallows) {
+            lines.push(`Disallow: ${disallow}`);
+          }
+        }
+
+        if (policy.crawlDelay) {
+          lines.push(`Crawl-delay: ${policy.crawlDelay}`);
+        }
+
+        lines.push(''); // Empty line between policies
+      }
+
+      // Add sitemaps
+      lines.push(`Sitemap: ${sitemapUrl}`);
+      for (const sitemap of additionalSitemaps) {
+        lines.push(`Sitemap: ${sitemap}`);
+      }
+
+      // Write robots.txt
+      const robotsPath = path.join(outDir, 'robots.txt');
+      fs.writeFileSync(robotsPath, lines.join('\n'));
+
+      console.log('Generated robots.txt');
+    },
+  };
+};
diff --git a/docs/src/components/FAQSchema.tsx 
b/docs/src/components/FAQSchema.tsx
new file mode 100644
index 00000000000..45a56b424b7
--- /dev/null
+++ b/docs/src/components/FAQSchema.tsx
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import type { JSX } from 'react';
+import Head from '@docusaurus/Head';
+
+interface FAQItem {
+  question: string;
+  answer: string;
+}
+
+interface FAQSchemaProps {
+  faqs: FAQItem[];
+}
+
+/**
+ * Component that injects FAQPage JSON-LD structured data
+ * Use this on FAQ pages to enable rich snippets in search results
+ *
+ * @example
+ * <FAQSchema faqs={[
+ *   { question: "What is Superset?", answer: "Apache Superset is..." },
+ *   { question: "How do I install it?", answer: "You can install via..." }
+ * ]} />
+ */
+export default function FAQSchema({ faqs }: FAQSchemaProps): JSX.Element | 
null {
+  // FAQPage schema requires a non-empty mainEntity array per schema.org specs
+  if (!faqs || faqs.length === 0) {
+    return null;
+  }
+
+  const schema = {
+    '@context': 'https://schema.org',
+    '@type': 'FAQPage',
+    mainEntity: faqs.map((faq) => ({
+      '@type': 'Question',
+      name: faq.question,
+      acceptedAnswer: {
+        '@type': 'Answer',
+        text: faq.answer,
+      },
+    })),
+  };
+
+  return (
+    <Head>
+      <script type="application/ld+json">{JSON.stringify(schema)}</script>
+    </Head>
+  );
+}
diff --git a/docs/src/components/TechArticleSchema.tsx 
b/docs/src/components/TechArticleSchema.tsx
new file mode 100644
index 00000000000..9a0fc049b4e
--- /dev/null
+++ b/docs/src/components/TechArticleSchema.tsx
@@ -0,0 +1,91 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import type { JSX } from 'react';
+import Head from '@docusaurus/Head';
+import { useLocation } from '@docusaurus/router';
+
+interface TechArticleSchemaProps {
+  title: string;
+  description: string;
+  datePublished?: string;
+  dateModified?: string;
+  keywords?: string[];
+  proficiencyLevel?: 'Beginner' | 'Expert';
+}
+
+/**
+ * Component that injects TechArticle JSON-LD structured data for 
documentation pages.
+ * This helps search engines understand technical documentation content.
+ *
+ * @example
+ * <TechArticleSchema
+ *   title="Installing Superset with Docker"
+ *   description="Learn how to install Apache Superset using Docker Compose"
+ *   keywords={['docker', 'installation', 'superset']}
+ *   proficiencyLevel="Beginner"
+ * />
+ */
+export default function TechArticleSchema({
+  title,
+  description,
+  datePublished,
+  dateModified,
+  keywords = [],
+  proficiencyLevel = 'Beginner',
+}: TechArticleSchemaProps): JSX.Element {
+  const location = useLocation();
+  const url = `https://superset.apache.org${location.pathname}`;
+
+  const schema = {
+    '@context': 'https://schema.org',
+    '@type': 'TechArticle',
+    headline: title,
+    description,
+    url,
+    proficiencyLevel,
+    author: {
+      '@type': 'Organization',
+      name: 'Apache Superset Contributors',
+      url: 'https://github.com/apache/superset/graphs/contributors',
+    },
+    publisher: {
+      '@type': 'Organization',
+      name: 'Apache Software Foundation',
+      url: 'https://www.apache.org/',
+      logo: {
+        '@type': 'ImageObject',
+        url: 'https://www.apache.org/foundation/press/kit/asf_logo.png',
+      },
+    },
+    mainEntityOfPage: {
+      '@type': 'WebPage',
+      '@id': url,
+    },
+    ...(datePublished && { datePublished }),
+    ...(dateModified && { dateModified }),
+    ...(keywords.length > 0 && { keywords: keywords.join(', ') }),
+  };
+
+  return (
+    <Head>
+      <script type="application/ld+json">{JSON.stringify(schema)}</script>
+    </Head>
+  );
+}
diff --git a/docs/static/img/superset-og-image.png 
b/docs/static/img/superset-og-image.png
new file mode 100644
index 00000000000..830fefaa6b0
Binary files /dev/null and b/docs/static/img/superset-og-image.png differ

Reply via email to