Skip to content
6 changes: 6 additions & 0 deletions .changeset/stale-peas-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@myst-theme/site': patch
'@myst-theme/book': patch
---

Add project.parts.primary_sidebar_footer option
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ _build

# yalc
.yalc
yalc.lock
yalc.lock
1 change: 1 addition & 0 deletions docs/_site/primary_sidebar_footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**Custom Footer** | [GitHub](https://github.com/jupyter-book/myst-theme)
1 change: 1 addition & 0 deletions docs/myst.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ project:
parts:
banner: _site/banner.md
footer: _site/footer.md
primary_sidebar_footer: _site/primary_sidebar_footer.md
toc:
- file: index.md
- file: examples.md
Expand Down
20 changes: 20 additions & 0 deletions docs/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,23 @@ project:

- Appears at the bottom of every page
- Supports any MyST markdown content (links, formatting, etc.)

## Sidebar Footer

Display custom content at the bottom of the primary sidebar (table of contents).

### Configuration

Create a markdown file with your sidebar footer content and add it to `myst.yml`:

```yaml
project:
parts:
primary_sidebar_footer: _site/primary_sidebar_footer.md
```

### Behavior

- Appears at the bottom of the primary sidebar (left-side navigation)
- If the `.md` file it points to is empty, the footer will not be visible
- If not configured, falls back to the default "Made with MyST" footer
15 changes: 15 additions & 0 deletions themes/book/app/components/SidebarFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { GenericParent } from 'myst-common';
import { MyST } from 'myst-to-react';
import { MadeWithMyst } from '@myst-theme/icons';

export function SidebarFooter({ content }: { content?: GenericParent }) {
if (!content) {
return <MadeWithMyst />;
}

return (
<div className="article footer myst-primary-sidebar-footer">
<MyST ast={content} />
</div>
);
}
3 changes: 2 additions & 1 deletion themes/book/app/routes/$.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { MadeWithMyst } from '@myst-theme/icons';
import { ArticlePage } from '../components/ArticlePage.js';
import { Footer } from '../components/Footer.js';
import { Banner } from '../components/Banner.js';
import { SidebarFooter } from '../components/SidebarFooter.js';
import type { TemplateOptions } from '../types.js';
import { useRouteError, isRouteErrorResponse } from '@remix-run/react';
type ManifestProject = Required<SiteManifest>['projects'][0];
Expand Down Expand Up @@ -102,7 +103,7 @@ function ArticlePageAndNavigationInternal({
<PrimaryNavigation
sidebarRef={toc}
hide_toc={hide_toc}
footer={<MadeWithMyst />}
footer={<SidebarFooter content={projectParts?.primary_sidebar_footer?.mdast} />}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a follow-up, we may want to rename footer here to something more descriptive, since this does not refer to the main page footer.

Maybe sidebarFooter.

Copy link
Contributor

@choldgraf choldgraf Nov 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, ideally this would be primarySidebarFooter and it could live alongside secondarySidebarFooter one day. Or, call it something more generic like primarySidebarEnd to recognize that other things than footers might be placed there one day.

I think my suggestion would be we just try to re-use the JB1 structure naming as much as possible for the book theme, since it has been used and battle-tested for a long time now:

https://pydata-sphinx-theme.readthedocs.io/en/stable/user_guide/layout.html

projectSlug={projectSlug}
/>
<TabStateProvider>
Expand Down
3 changes: 3 additions & 0 deletions themes/book/template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ parts:
- id: footer
description: The site wide footer
required: false
- id: primary_sidebar_footer
description: The primary sidebar footer, to replace Made with MyST
required: false
build:
install: npm install
start: npm run start
Expand Down
Loading