Welcome to Chapter 19! In this chapter, we’ll put our Rust-based Static Site Generator (SSG) to the test by building a full-fledged developer documentation site. This is a common and practical application for an SSG, requiring structured content, robust navigation, and efficient search capabilities. By completing this example, you’ll gain a deeper understanding of how to leverage all the features we’ve built, from content parsing and component rendering to routing and search indexing, to create a production-ready content platform.
Building a documentation site allows us to demonstrate the power of our SSG’s content organization, templating flexibility, and the integration of client-side interactivity via partial hydration. We’ll focus on designing a logical content hierarchy, generating dynamic sidebar navigation, and ensuring seamless search functionality. This step is crucial for validating our SSG’s capabilities in a real-world scenario and showcasing its extensibility for various content types.
Before we begin, ensure you have a working version of the SSG developed in previous chapters, especially those covering content parsing, Tera templating, component rendering, routing, and Pagefind integration. We’ll be building upon these foundations to construct our documentation portal. By the end of this chapter, you’ll have a fully functional, static documentation site ready for deployment, demonstrating the efficiency and performance benefits of our Rust SSG.
Planning & Design
A well-structured documentation site is critical for user experience. Our design will prioritize clear content organization, intuitive navigation, and efficient search.
Content Structure
We’ll adopt a hierarchical content structure, similar to many popular documentation platforms. This allows for logical grouping of topics and versioning.
content/docs/_index.md(Main documentation landing page)v1.0/_index.md(Version 1.0 landing)getting-started.mdinstallation.mdapi-reference.md
v2.0/_index.md(Version 2.0 landing)whats-new.mdgetting-started.mdadvanced/_index.md(Advanced topics landing)concepts.mdperformance.md
faq.md
Each Markdown file will include frontmatter to define its title, description, and crucially, a weight for ordering within its parent, and an optional layout to specify the docs.html template.
Templating and Navigation
We’ll create a dedicated docs.html Tera template. This template will feature:
- A header with the site title and potentially a version selector.
- A sidebar for hierarchical navigation, dynamically generated from the content structure.
- A main content area where the parsed Markdown will be rendered.
- A footer.
- Integration points for Pagefind search.
The core challenge for navigation is to traverse the processed Site content, identify all documentation pages, and build a nested data structure that Tera can easily iterate over to render the sidebar. This structure will need to respect the parent and weight fields from the frontmatter.
Component Architecture for Documentation Site
The diagram illustrates how Markdown files are ingested, parsed, and their frontmatter is used to collect metadata. This metadata is then used by the “Navigation System” to build a hierarchical tree, which is passed to the Tera templating engine. The docs.html template uses this data to render the sidebar, while the main content is rendered from the processed Markdown. Finally, Pagefind indexes the generated HTML for client-side search.
Step-by-Step Implementation
We will start by creating a new docs-site directory to house our example documentation project.
a) Setup/Configuration
First, let’s create the project structure for our documentation site.
Create the
docs-sitedirectory:mkdir docs-site cd docs-site mkdir content templates staticCopy the SSG CLI binary: Assuming your SSG’s compiled binary is named
ssg_cliand is located in../target/release/, copy it into thedocs-sitedirectory for convenience.cp ../target/release/ssg_cli .Explanation: We’re creating a self-contained example project. In a real scenario, you’d have
ssg_cliinstalled globally or usecargo run -- buildfrom the SSG’s root directory, specifying thedocs-siteas the project root.Create
config.tomlfor the docs site:# docs-site/config.toml base_url = "http://localhost:8000" title = "My Awesome Docs" description = "Documentation for My Awesome Project" default_template = "docs.html" # Default for all content content_dir = "content" templates_dir = "templates" static_dir = "static" output_dir = "public" pagefind_enabled = trueExplanation: This
config.tomlsets up basic site metadata and points to our documentation-specific directories. We’re settingdefault_templatetodocs.html, meaning all content will by default use this layout.Create initial content files: Let’s start with a few basic Markdown files to establish the hierarchy.
# docs-site/content/docs/_index.md +++ title = "Documentation Home" description = "Welcome to the documentation for My Awesome Project." weight = 1 +++ This is the main landing page for our documentation. Use the sidebar to navigate through topics.# docs-site/content/docs/v1.0/_index.md +++ title = "Version 1.0" description = "Documentation for version 1.0 of the project." weight = 10 +++ Explore the features and guides for version 1.0.# docs-site/content/docs/v1.0/getting-started.md +++ title = "Getting Started" description = "How to get started with My Awesome Project v1.0." weight = 20 +++ ## Installation To install, run: {{ CodeBlock(lang="bash", title="Install via Cargo") }} cargo add my-awesome-project {{ /CodeBlock }} ## First Steps Initialize your project with: ```bash my-awesome-project init```markdown # docs-site/content/docs/v2.0/_index.md +++ title = "Version 2.0 (Latest)" description = "Documentation for the latest version 2.0 of the project." weight = 10 +++ Welcome to the latest version! Discover new features and improvements.# docs-site/content/docs/v2.0/whats-new.md +++ title = "What's New in v2.0" description = "A summary of changes and new features in version 2.0." weight = 20 +++ Version 2.0 brings significant performance improvements and new API endpoints.Explanation: We’re setting up a
docssection with two versions. Notice theweightfield, which will be crucial for ordering the navigation. We’re also demonstrating the use of a custom componentCodeBlockwithin the Markdown.
b) Core Implementation
The main task here is to create the docs.html template and enhance our SSG to generate the navigation data for it.
Create
templates/docs.html: This will be our primary layout for all documentation pages.<!-- docs-site/templates/docs.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ page.title }} - {{ config.title }}</title> <meta name="description" content="{{ page.description | default(value=config.description) }}"> <link rel="stylesheet" href="/static/css/style.css"> <script src="/pagefind/pagefind-ui.js" type="text/javascript"></script> <style> body { font-family: sans-serif; margin: 0; display: flex; min-height: 100vh; } .sidebar { width: 280px; background-color: #f8f9fa; padding: 20px; border-right: 1px solid #e9ecef; overflow-y: auto; } .sidebar h2 { margin-top: 0; font-size: 1.2em; color: #343a40; } .sidebar ul { list-style: none; padding: 0; } .sidebar ul li { margin-bottom: 5px; } .sidebar ul li a { text-decoration: none; color: #007bff; } .sidebar ul li a:hover { text-decoration: underline; } .sidebar ul ul { margin-left: 15px; border-left: 1px solid #ced4da; padding-left: 10px; } .main-content { flex-grow: 1; padding: 40px; max-width: 900px; margin: 0 auto; } .main-content h1 { color: #343a40; } .main-content code { background-color: #e9ecef; padding: 2px 4px; border-radius: 3px; font-family: monospace; } .main-content pre { background-color: #f1f3f5; padding: 15px; border-radius: 5px; overflow-x: auto; } .pagefind-ui { margin-bottom: 20px; } /* Simple CodeBlock styling for demonstration */ .component-code-block { background-color: #282c34; color: #abb2bf; padding: 15px; border-radius: 5px; overflow-x: auto; margin-top: 1em; margin-bottom: 1em; } .component-code-block pre { margin: 0; } .component-code-block .title { color: #61afef; font-weight: bold; margin-bottom: 10px; display: block; } </style> </head> <body> <aside class="sidebar"> <div class="pagefind-ui" id="search"></div> <script> window.addEventListener('DOMContentLoaded', () => { new PagefindUI({ element: "#search", showImages: false, showEmptyFilters: false }); }); </script> <h2>{{ config.title }}</h2> <nav> {{ self::docs_nav() }} </nav> </aside> <main class="main-content"> {% if page.title %} <h1>{{ page.title }}</h1> {% endif %} {{ content | safe }} </main> </body> </html> {% macro docs_nav() %} <ul> {% for item in nav.items %} <li> <a href="{{ item.url }}">{{ item.title }}</a> {% if item.children %} <ul> {% for child in item.children %} <li><a href="{{ child.url }}">{{ child.title }}</a></li> {% endfor %} </ul> {% endif %} </li> {% endfor %} </ul> {% endmacro %}Explanation:
- This Tera template defines the basic HTML structure for our documentation.
- It includes a
sidebarandmain-contentarea. {{ page.title }}and{{ content | safe }}render the page-specific title and the HTML generated from Markdown.{{ config.title }}pulls the site title fromconfig.toml.- Crucially,
{{ self::docs_nav() }}calls a macro to render the navigation. This macro expects anavvariable in the Tera context, which will contain our hierarchical navigation data. - Basic CSS is embedded for quick styling. In a real project, this would be in
static/css/style.css. - Pagefind UI is initialized in the sidebar for search functionality.
- Basic styling for the
CodeBlockcomponent is also included.
Add
static/css/style.css(minimal):/* docs-site/static/css/style.css */ /* This file is referenced by docs.html. Add more styles here as needed. */ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; } a { color: #007bff; text-decoration: none; } a:hover { text-decoration: underline; } /* ... more styles for components, typography, etc. ... */Enhance SSG for Navigation Data Generation: This is the most complex part. We need to collect all pages, filter documentation pages, and build a hierarchical structure. We’ll add a new function to our SSG’s
BuildContextorSitestruct to achieve this.Let’s assume we have a
Sitestruct that holds allPageobjects. EachPagehasFrontMatterandrelative_path.First, define a struct to represent a navigation item.
// src/ssg/models.rs (or a new nav.rs module) // Add this struct if not already present use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NavItem { pub title: String, pub url: String, pub weight: i64, pub children: Vec<NavItem>, } impl NavItem { pub fn new(title: String, url: String, weight: i64) -> Self { Self { title, url, weight, children: Vec::new(), } } pub fn add_child(&mut self, child: NavItem) { self.children.push(child); self.children.sort_by_key(|c| c.weight); // Keep children sorted } }Explanation:
NavItemwill represent a single link in our navigation. It has atitle,url,weightfor ordering, andchildrento support nested navigation.Now, let’s modify the
Sitestruct and add a method to build this navigation tree. This method will typically be called during the build process before individual pages are rendered.// src/ssg/site.rs (or build_context.rs) use std::path::{Path, PathBuf}; use std::collections::BTreeMap; // For stable ordering use crate::ssg::content::{Page, FrontMatter}; // Assuming Page and FrontMatter are here // ... other imports // Assuming these structs exist from previous chapters // pub struct FrontMatter { ... pub weight: Option<i64>, pub layout: Option<String>, ... } // pub struct Page { ... pub front_matter: FrontMatter, pub relative_path: PathBuf, pub permalink: String, ... } pub struct Site { pub config: Config, pub pages: BTreeMap<PathBuf, Page>, // Map from original path to Page object // ... other fields } impl Site { // ... existing methods like new, load_content, etc. /// Builds a hierarchical navigation tree for documentation pages. /// Returns a Vec of top-level NavItems. pub fn build_docs_navigation(&self) -> Vec<NavItem> { let mut nav_map: BTreeMap<PathBuf, NavItem> = BTreeMap::new(); let mut parents: BTreeMap<PathBuf, PathBuf> = BTreeMap::new(); // First pass: Create all NavItems and identify their parents for (original_path, page) in &self.pages { // Only consider pages under 'content/docs/' for documentation navigation if !original_path.starts_with(&self.config.content_dir.join("docs")) { continue; } let title = page.front_matter.title.clone().unwrap_or_else(|| { original_path.file_stem().unwrap().to_string_lossy().into_owned() }); let url = page.permalink.clone(); let weight = page.front_matter.weight.unwrap_or(0); let nav_item = NavItem::new(title, url, weight); nav_map.insert(original_path.clone(), nav_item); // Determine parent path for hierarchical structure if let Some(parent_path) = original_path.parent() { // Special handling for _index.md to ensure its parent is its own directory let current_dir = if original_path.file_name().map_or(false, |name| name == "_index.md") { original_path.parent().map(|p| p.to_path_buf()) } else { original_path.parent().map(|p| p.to_path_buf()) }; if let Some(dir) = current_dir { if dir != self.config.content_dir.join("docs") && dir != self.config.content_dir { parents.insert(original_path.clone(), dir.clone()); } } } } // Second pass: Assemble the tree let mut root_items: Vec<NavItem> = Vec::new(); let mut processed_children: BTreeMap<PathBuf, Vec<NavItem>> = BTreeMap::new(); // Collect all items that have a parent for (child_path, parent_path) in &parents { if let Some(child_item) = nav_map.remove(child_path) { // Remove to avoid adding to root later processed_children.entry(parent_path.clone()) .or_default() .push(child_item); } } // Attach processed children to their parents for (parent_path, children) in processed_children { if let Some(parent_item) = nav_map.get_mut(&parent_path) { for child in children { parent_item.add_child(child); } } else { // If parent_path isn't in nav_map (e.g., it's a directory without an _index.md), // we might need to create a dummy NavItem for it or handle it as a top-level. // For now, these children might be orphaned if their parent has no _index.md. // A robust solution would create dummy NavItems for directories. // For simplicity, let's assume all parent directories have an _index.md. warn!("Parent path {:?} not found in nav_map. Children might be orphaned.", parent_path); } } // Any remaining items in nav_map are root-level or _index.md files whose parent is 'docs' for (path, item) in nav_map { // Only add to root if its parent is the base 'docs' directory if path.parent().map_or(false, |p| p == self.config.content_dir.join("docs")) { root_items.push(item); } else if path == self.config.content_dir.join("docs").join("_index.md") { // Handle the main docs _index.md as a root item root_items.push(item); } } root_items.sort_by_key(|item| item.weight); root_items } }Explanation:
build_docs_navigationiterates through all pages inself.pages.- It filters for pages within
content/docs/. - It creates a
NavItemfor each relevant page, using itstitle,permalink, andweight. - It uses two
BTreeMaps:nav_mapto store allNavItems by their original path, andparentsto track parent-child relationships. - The logic attempts to identify the correct parent directory for each page, handling
_index.mdfiles which represent the “root” of their directory. - In the second pass, it iterates through the
parentsmap to attach children to their respective parentNavItems. - Finally, any remaining
NavItems innav_mapthat are top-level documentation pages (e.g., directly undercontent/docs/orcontent/docs/_index.md) are added toroot_itemsand sorted byweight.
Self-Correction/Refinement: The navigation generation logic for complex directory structures can be tricky. The provided code gives a basic hierarchical structure. For truly robust navigation (e.g., handling directories without
_index.mdas implicit navigation nodes), you might need to:- Pre-scan the
content/docsdirectory to identify all actual directories. - For each directory, if an
_index.mdexists, use its frontmatter. Otherwise, create a defaultNavItemfor the directory itself. - Then, attach pages and subdirectories as children.
This approach ensures all parts of the hierarchy are navigable, even if not every directory has an
_index.md. For this example, we’ll assume_index.mdfiles define the directory’s presence in navigation.
Now, we need to integrate this into our main build process.
// src/ssg/mod.rs (or main.rs where the build logic resides) // ... other imports use crate::ssg::site::{Site, NavItem}; // Assuming NavItem is in site.rs for now, or its own module use tera::{Context, Tera}; use std::fs; use std::path::{Path, PathBuf}; pub fn build_site(config: &Config) -> Result<(), Box<dyn std::error::Error>> { info!("Starting site build..."); // 1. Load content let mut site = Site::new(config.clone()); site.load_content()?; // This populates site.pages // 2. Initialize Tera let tera = Tera::new(&format!("{}/**/*.html", config.templates_dir))?; // 3. Build docs navigation let docs_navigation = site.build_docs_navigation(); debug!("Generated documentation navigation: {:?}", docs_navigation); // 4. Create output directory let output_path = PathBuf::from(&config.output_dir); if output_path.exists() { fs::remove_dir_all(&output_path)?; } fs::create_dir_all(&output_path)?; // 5. Copy static files // ... (existing static file copy logic) // 6. Render pages for (original_path, page) in site.pages.iter() { info!("Rendering page: {}", page.relative_path.display()); let output_file_path = output_path.join(&page.output_file_path); let output_dir = output_file_path.parent().ok_or("Invalid output path")?; fs::create_dir_all(output_dir)?; let mut context = Context::new(); context.insert("config", &site.config); context.insert("page", &page); context.insert("content", &page.rendered_html); // Insert the generated navigation data into the Tera context context.insert("nav", &docs_navigation); // 'nav' matches the macro in docs.html // Determine which template to use let template_name = page.front_matter.layout.as_ref() .unwrap_or(&config.default_template) .to_string(); let rendered = tera.render(&template_name, &context)?; fs::write(&output_file_path, rendered.as_bytes())?; info!("Rendered {} to {}", page.relative_path.display(), output_file_path.display()); } // 7. Run Pagefind indexing if config.pagefind_enabled { crate::ssg::pagefind::run_pagefind_indexing(&output_path)?; } info!("Site build complete!"); Ok(()) }Explanation:
- We call
site.build_docs_navigation()after loading content and before rendering pages. - The result,
docs_navigation, is then inserted into the TeraContextunder the key"nav". This makes it accessible to ourdocs_nav()macro indocs.html. - The
template_nameis now dynamically selected frompage.front_matter.layoutor falls back toconfig.default_template(docs.htmlin this case).
c) Testing This Component
Build the documentation site: Navigate to your
docs-sitedirectory in the terminal../ssg_cli buildYou should see log messages indicating pages are being processed and rendered.
Verify generated files: Check the
docs-site/publicdirectory. You should find:public/index.html(forcontent/docs/_index.md)public/v1.0/index.htmlpublic/v1.0/getting-started/index.htmlpublic/v2.0/index.htmlpublic/v2.0/whats-new/index.htmlpublic/static/css/style.csspublic/pagefind/directory with search index files.
Serve the site locally: You can use a simple static file server (like Python’s
http.serverorlive-servernpm package) to view the generated site.# If you have Python installed python3 -m http.server --directory public 8000Open your browser to
http://localhost:8000.Expected Behavior:
- You should see the “Documentation Home” page.
- A sidebar should be present on the left, displaying “My Awesome Project” and the navigation links:
- Documentation Home
- Version 1.0
- Getting Started
- Version 2.0 (Latest)
- What’s New in v2.0
- Clicking on “Getting Started” should take you to that page, and you should see the
CodeBlockcomponent rendered correctly with syntax highlighting (if your component renderer supports it, or with the basic styling we added). - The Pagefind search bar should be visible and functional. Try searching for “installation” or “api”.
Production Considerations
Error Handling:
- Missing
weightortitle: Our navigation generation gracefully defaultsweightto0andtitleto the file stem. For production, you might want to enforce these fields for documentation via a schema validation for frontmatter (perhaps usingserde_json::from_valueand a custom schema). - Orphaned Navigation Items: The current navigation logic assumes directories have
_index.mdto be present in the navigation. If a directorycontent/docs/orphan/exists without_index.md, its children might not appear. A more robust solution would infer navigation items for directories without_index.md. - Broken Permalinks: Ensure the permalink generation is robust and handles special characters or very deep nesting.
- Missing
Performance Optimization:
- Navigation Tree Generation: For sites with thousands of documentation pages,
build_docs_navigationcould become a bottleneck. Consider caching the generatedNavItemtree if the content hasn’t changed. Our incremental build system from Chapter 18 could detect changes incontent/docsand trigger a rebuild of only the navigation data. - Tera Template Caching: Tera already caches compiled templates.
- Asset Optimization: Minify CSS/JS in the
staticdirectory. Our SSG could integrate with a tool likelightningcssorterserfor this.
- Navigation Tree Generation: For sites with thousands of documentation pages,
Security Considerations:
- Content Sanitization:
pulldown-cmarkgenerally outputs safe HTML, but if you allow raw HTML or custom components that inject raw HTML, ensure proper sanitization to prevent XSS attacks. Our component rendering should always escape user input unless explicitly designed for raw HTML (e.g., aRawHtmlcomponent). - Pagefind: Pagefind is a client-side search, so it doesn’t pose direct server-side security risks, but ensure the content being indexed is appropriate.
- Content Sanitization:
Logging and Monitoring:
- Log the time taken for navigation generation and overall build process.
- Monitor build failures and page rendering errors, especially for complex documentation structures. Use
tracingorlogmacros effectively.
Deployment:
- CDN: Static documentation sites are ideal for deployment on Content Delivery Networks (CDNs) like Cloudflare Pages, Netlify, Vercel, or AWS S3/CloudFront. This provides excellent global performance and scalability.
- CI/CD: Set up a CI/CD pipeline (e.g., GitHub Actions, GitLab CI) to automatically build and deploy the documentation site whenever changes are pushed to the
mainbranch of your content repository. This ensures your documentation is always up-to-date.
Code Review Checkpoint
At this point, you should have:
- Files created/modified in
docs-site:config.toml: Configures the documentation site.content/docs/_index.md: Main docs landing page.content/docs/v1.0/_index.md,content/docs/v1.0/getting-started.md: Example v1.0 content.content/docs/v2.0/_index.md,content/docs/v2.0/whats-new.md: Example v2.0 content.templates/docs.html: The main Tera template for documentation pages, including navigation rendering logic and Pagefind integration.static/css/style.css: Minimal CSS for styling.
- Files modified in your SSG (
src/ssg/):src/ssg/models.rs(orsrc/ssg/nav.rs): AddedNavItemstruct.src/ssg/site.rs(orsrc/ssg/build_context.rs): Addedbuild_docs_navigationmethod toSite(orBuildContext) to generate the hierarchical navigation data.src/ssg/mod.rs(orsrc/main.rs): Modified thebuild_sitefunction to callbuild_docs_navigationand pass the resultingVec<NavItem>to the Tera context for rendering.
This checkpoint ensures that the SSG can now process a structured set of Markdown files, generate a dynamic navigation menu, and render them using a specific documentation layout, all while integrating client-side search.
Common Issues & Solutions
Issue: Navigation links are empty or incorrect.
- Cause: This usually indicates an issue with
permalinkgeneration or thebuild_docs_navigationlogic. - Debugging:
- Add
debug!logs inbuild_docs_navigationto inspect thenav_map,parents, androot_itemsat each stage. - Print the
page.permalinkfor each page during rendering. - Verify the
urlfield ofNavIteminstances before they are passed to Tera. - Check for incorrect
base_urlinconfig.toml.
- Add
- Solution: Carefully review the
build_docs_navigationlogic, especially how parent paths are determined and how_index.mdfiles are handled. Ensurepage.permalinkis correctly generated for all content files.
- Cause: This usually indicates an issue with
Issue: Custom components (e.g.,
CodeBlock) are not rendering or appear as raw text.- Cause: The component rendering pipeline might not be correctly integrated or the component definition is missing/malformed.
- Debugging:
- Check the SSG’s logs during Markdown processing for any errors related to component parsing or rendering.
- Inspect the
page.rendered_htmlvariable before it’s passed to Tera. Does it contain the expected HTML output for the component, or the raw{{ CodeBlock(...) }}string? - Verify that
content | safeis used indocs.htmlto prevent Tera from escaping the HTML generated by our component renderer.
- Solution: Ensure your
ContentProcessor(or equivalent) correctly identifies and replaces component syntax with rendered HTML. Confirm that theTeracontext is receiving the processed HTML.
Issue: Pagefind search is not working or says “No results”.
- Cause: Pagefind indexing might not be running, or the
pagefind-ui.jsscript is not correctly loaded. - Debugging:
- Check the console output during the build process to confirm
run_pagefind_indexingwas called without errors. - Verify that the
public/pagefinddirectory exists and contains the necessary index files. - Open your browser’s developer console on the live docs site and check the Network tab for
pagefind-ui.jsloading errors. Also, check the Console tab for any JavaScript errors related to Pagefind initialization. - Ensure the
pagefind-uielement ID (#searchin our example) matches the ID in yourdocs.htmltemplate.
- Check the console output during the build process to confirm
- Solution: Double-check the
pagefind_enabledflag inconfig.toml. Ensure thepagefindintegration code insrc/ssg/pagefind.rsis correct and therun_pagefind_indexingfunction is called. Verify the script path and initialization indocs.html.
- Cause: Pagefind indexing might not be running, or the
Testing & Verification
To thoroughly test and verify the documentation site:
Full Build:
- Run
./ssg_cli build(orcargo run -- buildif running from the SSG’s root) from thedocs-sitedirectory. - Ensure no errors are reported in the console.
- Run
Local Server:
- Serve the
publicdirectory usingpython3 -m http.server --directory public 8000or a similar tool. - Open
http://localhost:8000in your web browser.
- Serve the
Content Verification:
- Navigation:
- Verify the sidebar navigation loads correctly.
- Ensure all expected pages (Documentation Home, v1.0, v2.0, Getting Started, What’s New) are listed.
- Check that the hierarchy is correct (e.g., “Getting Started” is nested under “Version 1.0”).
- Confirm that clicking each navigation link takes you to the correct page.
- Check the order of items within a level based on their
weightin frontmatter.
- Page Content:
- Navigate to
Getting Startedand verify theCodeBlockcomponent renders correctly. - Check for any Markdown rendering issues (e.g., lists, links, images).
- Navigate to
- Frontmatter: Verify that the page title and description are correctly displayed (e.g., in the browser tab title and meta description).
- Navigation:
Search Functionality:
- Use the Pagefind search bar in the sidebar.
- Search for keywords present in your documentation content (e.g., “installation”, “API”, “new features”).
- Verify that relevant search results appear and link to the correct pages.
Responsiveness (Optional but Recommended):
- Resize your browser window or use developer tools to simulate mobile devices.
- Ensure the layout remains usable and readable on smaller screens. (Our current CSS is basic, so this might require more styling work).
By following these steps, you can confidently verify that your SSG is capable of generating a robust and functional developer documentation site.
Summary & Next Steps
In this chapter, we successfully built a real-world developer documentation site using our Rust-based Static Site Generator. We designed a hierarchical content structure, implemented dynamic sidebar navigation, and integrated client-side search with Pagefind. This exercise validated many core features of our SSG, demonstrating its ability to handle structured content, flexible templating, and interactive components in a production-ready context.
We delved into the specifics of:
- Configuring a documentation project.
- Creating a dedicated
docs.htmlTera template. - Enhancing our SSG’s build process to generate and pass hierarchical navigation data to templates.
- Setting up example documentation content with custom components.
- Testing and verifying the generated site’s functionality.
This chapter showcased how our SSG can be adapted for specific use cases, emphasizing the importance of content organization and efficient data processing for large content sets.
In the next chapter, we will continue building real-world examples by tackling a Learning Platform with Structured Chapters. This will further challenge our SSG’s content management, routing, and sequential navigation capabilities, potentially introducing concepts like progress tracking and lesson dependencies.