diff --git a/blog/2026-01-09-top-python-web-frameworks-2026.md b/blog/2026-01-09-top-python-web-frameworks-2026.md index a7395f47a..221e1ca1a 100644 --- a/blog/2026-01-09-top-python-web-frameworks-2026.md +++ b/blog/2026-01-09-top-python-web-frameworks-2026.md @@ -1,13 +1,21 @@ --- author: Tom Gotsman +author_bio: Tom Gotsman is a Software Engineer at Reflex. date: 2026-01-09 +updated_at: 2026-01-09 title: Top Python Web Development Frameworks in 2026 +title_tag: Best Python Web Frameworks 2026 - Reflex, Django, Flask & More description: Reflex vs Django vs Flask vs Gradio vs Streamlit vs Dash vs FastAPI image: /blog/top_python_web_frameworks_2026.png tag: Builder meta: [ {"name": "keywords", "content": "streamlit python, streamlit, streamlit alternatives, plotly, dash app, plotly python, fastapi"}, ] +faq: [ + {"question": "What is the best Python web framework in 2026?", "answer": "It depends on your use case. Reflex is ideal for full-stack apps in pure Python, Django for large-scale applications, Flask for lightweight APIs, and FastAPI for high-performance async APIs."}, + {"question": "Can I build a full-stack web app with only Python?", "answer": "Yes. Frameworks like Reflex let you build both the frontend and backend entirely in Python without writing JavaScript."}, + {"question": "What is the difference between Streamlit and Reflex?", "answer": "Streamlit is designed for quick data apps and dashboards, while Reflex is a full-stack framework for building production-grade, customizable web applications entirely in Python."}, +] --- diff --git a/pcweb/meta/meta.py b/pcweb/meta/meta.py index fd06dc52d..1b9d25371 100644 --- a/pcweb/meta/meta.py +++ b/pcweb/meta/meta.py @@ -1,3 +1,5 @@ +import json + import reflex as rx from pcweb.constants import REFLEX_DOMAIN, REFLEX_DOMAIN_URL, TWITTER_CREATOR @@ -105,3 +107,79 @@ def create_meta_tags( image=image_url, url=page_url, ) + + +def _normalize_image_url(image: str) -> str: + """Ensure image path is a full URL.""" + if image and not image.startswith(("http://", "https://")): + return f"https://reflex.dev{'' if image.startswith('/') else '/'}{image}" + return image + + +def blog_jsonld( + title: str, + description: str, + author: str, + date: str, + image: str, + url: str, + faq: list[dict[str, str]] | None = None, + author_bio: str | None = None, + updated_at: str | None = None, +) -> rx.Component: + """Create a single JSON-LD script tag with @graph for a blog post. + + Always includes a BlogPosting entry. If faq items are provided, + a FAQPage entry is also added to the graph. + """ + author_node: dict = {"@type": "Person", "name": author} + if author_bio: + author_node["description"] = author_bio + + posting: dict = { + "@type": "BlogPosting", + "headline": title, + "description": description, + "image": _normalize_image_url(image), + "datePublished": str(date), + "author": author_node, + } + if updated_at: + posting["dateModified"] = str(updated_at) + + graph: list[dict] = [ + { + **posting, + "publisher": { + "@type": "Organization", + "name": "Reflex", + "url": REFLEX_DOMAIN_URL, + }, + "mainEntityOfPage": { + "@type": "WebPage", + "@id": url, + }, + }, + ] + if faq: + graph.append( + { + "@type": "FAQPage", + "mainEntity": [ + { + "@type": "Question", + "name": item["question"], + "acceptedAnswer": { + "@type": "Answer", + "text": item["answer"], + }, + } + for item in faq + ], + } + ) + data = { + "@context": "https://schema.org", + "@graph": graph, + } + return rx.el.script(json.dumps(data), type="application/ld+json") diff --git a/pcweb/pages/blog/blog.py b/pcweb/pages/blog/blog.py index 2755cf1f0..3a3f0e0ef 100644 --- a/pcweb/pages/blog/blog.py +++ b/pcweb/pages/blog/blog.py @@ -211,13 +211,15 @@ def blogs(): # Get the docpage component. route = f"/blog/{path}" title = rx.utils.format.to_snake_case(path.rsplit("/", 1)[1].replace(".md", "")) + # Use title_tag for and og/twitter if provided, otherwise fall back to title. + seo_title = document.metadata.get("title_tag") or document.metadata["title"] comp = marketing_page( path=route, - title=document.metadata["title"] + " ยท Reflex Blog", + title=seo_title, description=document.metadata["description"], image=document.metadata["image"], meta=create_meta_tags( - title=document.metadata["title"], + title=seo_title, description=document.metadata["description"], image=document.metadata["image"], url=f"https://reflex.dev{route}", diff --git a/pcweb/pages/blog/page.py b/pcweb/pages/blog/page.py index 080abdbaa..14891c1cf 100644 --- a/pcweb/pages/blog/page.py +++ b/pcweb/pages/blog/page.py @@ -7,6 +7,7 @@ from pcweb.components.marketing_button import button from pcweb.constants import REFLEX_URL from pcweb.flexdown import xd2 as xd +from pcweb.meta.meta import blog_jsonld from pcweb.templates.docpage import get_toc, right_sidebar_item_highlight from .paths import blog_data @@ -183,7 +184,21 @@ def page(document, route) -> rx.Component: toc, _ = get_toc(document, route) toc = [(level, text) for level, text in toc if level <= 3] page_url = f"{REFLEX_URL.strip('/')}{route}" + + jsonld_script = blog_jsonld( + title=meta["title"], + description=meta["description"], + author=meta["author"], + date=str(meta["date"]), + image=meta["image"], + url=page_url, + faq=meta.get("faq"), + author_bio=meta.get("author_bio"), + updated_at=str(meta["updated_at"]) if meta.get("updated_at") else None, + ) + return rx.el.section( + jsonld_script, rx.el.article( rx.el.div( rx.el.div(