Skip to content

Commit fabd082

Browse files
committed
Tips & Tricks for advanced Mintlify pages
1 parent 2d4b2da commit fabd082

4 files changed

Lines changed: 276 additions & 43 deletions

File tree

src/CloudNimble.DotNetDocs.Docs/CloudNimble.DotNetDocs.Docs.docsproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="DotNetDocs.Sdk/1.4.1">
1+
<Project Sdk="DotNetDocs.Sdk/1.5.1">
22

33
<PropertyGroup>
44
<DocumentationType>Mintlify</DocumentationType>
@@ -67,6 +67,7 @@
6767
<Group Name="Mintlify" Icon="/images/icons/mintlify.svg" Tag="PARTNER">
6868
<Pages>
6969
providers/mintlify/index;
70+
providers/mintlify/tips-and-tricks;
7071
providers/mintlify/navigation;
7172
providers/mintlify/collections;
7273
providers/mintlify/dotnet-library

src/CloudNimble.DotNetDocs.Docs/docs.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
"default": "dark"
44
},
55
"colors": {
6+
"primary": "#419AC5",
67
"dark": "#3CD0E2",
7-
"light": "#419AC5",
8-
"primary": "#419AC5"
8+
"light": "#419AC5"
99
},
1010
"favicon": "/images/icons/favicon-96x96.png",
1111
"integrations": {
@@ -63,6 +63,7 @@
6363
"icon": "/images/icons/mintlify.svg",
6464
"pages": [
6565
"providers/mintlify/index",
66+
"providers/mintlify/tips-and-tricks",
6667
"providers/mintlify/navigation",
6768
"providers/mintlify/collections",
6869
"providers/mintlify/dotnet-library"

src/CloudNimble.DotNetDocs.Docs/providers/mintlify/index.mdx

Lines changed: 5 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ icon: sparkles
1515
<Card title="Context-Aware Icons" icon="icons" href="#icon-system">
1616
50+ FontAwesome icons automatically assigned based on type characteristics and naming patterns
1717
</Card>
18-
<Card title="React Components" icon="react" href="#react-components">
19-
Embed custom React components directly in your MDX files for interactive documentation
18+
<Card title="Tips & Tricks" icon="lightbulb" href="/providers/mintlify/tips-and-tricks">
19+
React components, hydration fixes, page modes, and custom landing page patterns
2020
</Card>
2121
</CardGroup>
2222

@@ -184,44 +184,6 @@ services.AddDotNetDocsMintlify(options =>
184184
Set `NavigationType` to control whether the root project appears in main navigation (`Pages`), as a top-level tab (`Tabs`), or in the products section (`Products`). This is particularly useful for multi-project documentation collections.
185185
</Info>
186186

187-
### React Component Support
188-
189-
Mintlify MDX supports embedding custom React components directly in your documentation:
190-
191-
- Import components from the `snippets` folder
192-
- Use React hooks (useState, useEffect, etc.)
193-
- Create interactive demos, calculators, and visualizations
194-
- Build reusable component libraries
195-
196-
```jsx snippets/ColorPicker.jsx
197-
export const ColorPicker = () => {
198-
const [color, setColor] = React.useState('#3CD0E2');
199-
200-
return (
201-
<div>
202-
<input
203-
type="color"
204-
value={color}
205-
onChange={(e) => setColor(e.target.value)}
206-
/>
207-
<p>Selected: {color}</p>
208-
</div>
209-
);
210-
};
211-
```
212-
213-
```mdx Using the Component
214-
import { ColorPicker } from '/snippets/ColorPicker';
215-
216-
## Try It Out
217-
218-
<ColorPicker />
219-
```
220-
221-
<Info>
222-
React components must be placed in the `snippets` folder and exported as named exports. You cannot import components from arbitrary MDX files.
223-
</Info>
224-
225187
## Getting Started
226188

227189
<Steps>
@@ -543,6 +505,9 @@ docs/
543505
## See Also
544506

545507
<CardGroup cols={2}>
508+
<Card title="Tips & Tricks" icon="lightbulb" href="/providers/mintlify/tips-and-tricks">
509+
React components, hydration fixes, page modes, and custom landing page patterns
510+
</Card>
546511
<Card title="Collections" icon="layer-group" href="/providers/mintlify/collections">
547512
Learn how Mintlify handles multi-project documentation with path rewriting and resource relocation
548513
</Card>
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
---
2+
title: Tips & Tricks
3+
sidebarTitle: Tips & Tricks
4+
description: Practical patterns for building advanced pages in Mintlify
5+
icon: lightbulb
6+
---
7+
8+
## React Component Support
9+
10+
Mintlify MDX supports embedding custom React components directly in your documentation:
11+
12+
- Import components from the `snippets` folder
13+
- Use React hooks (useState, useEffect, etc.)
14+
- Create interactive demos, calculators, and visualizations
15+
- Build reusable component libraries
16+
17+
```jsx snippets/ColorPicker.jsx
18+
export const ColorPicker = () => {
19+
const [color, setColor] = React.useState('#3CD0E2');
20+
21+
return (
22+
<div>
23+
<input
24+
type="color"
25+
value={color}
26+
onChange={(e) => setColor(e.target.value)}
27+
/>
28+
<p>Selected: {color}</p>
29+
</div>
30+
);
31+
};
32+
```
33+
34+
```mdx Using the Component
35+
import { ColorPicker } from '/snippets/ColorPicker';
36+
37+
## Try It Out
38+
39+
<ColorPicker />
40+
```
41+
42+
<Info>
43+
React components must be placed in the `snippets` folder and exported as named exports. You cannot import components from arbitrary MDX files.
44+
</Info>
45+
46+
### Preventing the Double Flash
47+
48+
Mintlify pages render server-side first, then hydrate on the client. React components that modify layout or inject dynamic content cause a **double flash**: the server-rendered markup appears, disappears during hydration, then re-renders with client state. This creates a jarring flicker.
49+
50+
The fix: render your component **invisible** on mount, then fade it in after hydration completes.
51+
52+
<Tabs>
53+
<Tab title="Pattern">
54+
Every client-side React component should follow this three-part pattern:
55+
56+
1. **State gate**: `useState(false)` tracks whether the component has mounted
57+
2. **Mount trigger**: `useEffect(() => set(true), [])` fires once after hydration
58+
3. **Opacity transition**: the wrapper starts at `opacity: 0` and transitions to `1`
59+
60+
```jsx snippets/MyComponent.jsx
61+
export const MyComponent = () => {
62+
const [ready, setReady] = React.useState(false);
63+
64+
React.useEffect(() => {
65+
setReady(true);
66+
}, []);
67+
68+
return (
69+
<div style={{
70+
opacity: ready ? 1 : 0,
71+
transition: 'opacity 0.5s ease-out'
72+
}}>
73+
{/* your content */}
74+
</div>
75+
);
76+
};
77+
```
78+
79+
<Warning>
80+
Never use `display: none` or `visibility: hidden` for this purpose. Those properties trigger layout reflows when they change, causing the content to "jump" into place. The `opacity` + `transition` approach is GPU-accelerated and reflow-free.
81+
</Warning>
82+
</Tab>
83+
84+
<Tab title="With Slide Animation">
85+
Combine the opacity gate with a CSS transform for a polished entrance. Use `backfaceVisibility: 'hidden'` and `translateZ(0)` to keep the animation on the GPU compositing layer.
86+
87+
```jsx snippets/SlideInSection.jsx
88+
export const SlideInSection = ({ children, direction = 'left' }) => {
89+
const [ready, setReady] = React.useState(false);
90+
91+
React.useEffect(() => {
92+
setReady(true);
93+
}, []);
94+
95+
const offset = direction === 'left' ? '-50px' : '50px';
96+
97+
return (
98+
<div style={{
99+
opacity: ready ? 1 : 0,
100+
transform: ready ? 'translateX(0) translateZ(0)' : `translateX(${offset}) translateZ(0)`,
101+
transition: 'opacity 0.8s ease-out, transform 0.8s ease-out',
102+
backfaceVisibility: 'hidden'
103+
}}>
104+
{children}
105+
</div>
106+
);
107+
};
108+
```
109+
</Tab>
110+
111+
<Tab title="Page Modes">
112+
Mintlify has four page modes set via frontmatter. Choose based on how much chrome you need:
113+
114+
| Mode | Sidebar | Table of Contents | Footer | Top Navbar | Use Case |
115+
|------|---------|-------------------|--------|------------|----------|
116+
| *(default)* | Yes | Yes | Yes | Yes | Standard documentation pages |
117+
| `wide` | Yes | Hidden | Yes | Yes | Pages with tables, diagrams, or wide content |
118+
| `center` | Hidden | Hidden | Yes | Yes | Text-heavy pages like changelogs |
119+
| `custom` | Hidden | Hidden | Hidden | **Yes** | Landing pages, branded layouts |
120+
121+
```mdx
122+
---
123+
title: "My Page"
124+
mode: "wide"
125+
---
126+
```
127+
128+
<Note>
129+
All modes preserve the **top navbar**. There is no built-in way to hide it. The `custom` mode is the most minimal — it gives you a blank canvas with only the top nav remaining.
130+
</Note>
131+
</Tab>
132+
133+
<Tab title="Custom Mode Landing Pages">
134+
The DotNetDocs home page uses `mode: "custom"` — Mintlify hides the sidebar, table of contents, and footer automatically, leaving only the top navbar. Your React components fill the page.
135+
136+
However, Mintlify still applies content width constraints and renders auto-generated prose elements (the `h1` from `title` and `p` from `description` in frontmatter). A CSS wrapper class handles these:
137+
138+
```mdx index.mdx
139+
---
140+
title: My Docs - Landing Page
141+
sidebarTitle: Home
142+
mode: "custom"
143+
icon: house
144+
---
145+
146+
import { Hero } from '/snippets/Hero.jsx'
147+
import { Features } from '/snippets/Features.jsx'
148+
import { CTA } from '/snippets/CTA.jsx'
149+
150+
<div className="custom-landing">
151+
<Hero />
152+
<Features />
153+
<CTA />
154+
</div>
155+
```
156+
157+
```css style.css
158+
/* Remove content width constraints so components go edge-to-edge */
159+
.custom-landing main,
160+
.custom-landing article {
161+
max-width: 100% !important;
162+
padding: 0 !important;
163+
}
164+
165+
/* Suppress the auto-generated h1 and description from frontmatter */
166+
.custom-landing .prose > h1:first-child,
167+
.custom-landing .prose > p:first-child {
168+
display: none;
169+
}
170+
```
171+
172+
The result at runtime:
173+
174+
```plaintext
175+
┌──────────────────────────────────────────┐
176+
│ Mintlify Top Navbar │ ← Always visible (all modes)
177+
├──────────────────────────────────────────┤
178+
│ │
179+
│ <Hero /> ← Full-width canvas │
180+
│ <Features /> ← No sidebar or TOC │
181+
│ <CTA /> ← No footer │
182+
│ │
183+
└──────────────────────────────────────────┘
184+
```
185+
186+
<Warning>
187+
Mintlify warns that **inline `style` props on custom mode pages cause layout shift on page load**. Use Tailwind classes or a custom CSS file instead of `style={{...}}` for layout properties like width, padding, and margins. The `opacity`/`transform` pattern for flash prevention is fine because those properties don't affect layout.
188+
</Warning>
189+
190+
<Note>
191+
The `sidebarTitle` frontmatter field controls what label appears in the sidebar when users are on *other* pages. Even though the sidebar is hidden on the custom page itself, this title still matters for navigation elsewhere.
192+
</Note>
193+
</Tab>
194+
</Tabs>
195+
196+
<Tip>
197+
**Rules of thumb:**
198+
- Only `opacity` and `transform` are safe for mount animations — they don't trigger reflows
199+
- Always use `ease-out` timing — it feels responsive because the fast phase happens first
200+
- Keep transitions under 0.8s — longer feels sluggish, shorter can still flash
201+
- Use `translateZ(0)` to force GPU compositing, even if you're not animating position
202+
</Tip>
203+
204+
### Loading External Scripts
205+
206+
Mintlify doesn't support `<script>` tags directly in MDX. Instead, use Mintlify's built-in `load()` function to fetch external libraries at runtime. Chain multiple `load()` calls with `.then()` to guarantee dependency order, and guard your initialization against Mintlify's hydration timing.
207+
208+
```jsx snippets/AnimatedSection.jsx
209+
export const AnimatedSection = ({ children }) => {
210+
const [ready, setReady] = React.useState(false);
211+
212+
React.useEffect(() => {
213+
load('https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js')
214+
.then(function () {
215+
return load('https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/ScrollTrigger.min.js');
216+
})
217+
.then(function () {
218+
// Wait for DOM to be fully ready
219+
if (document.readyState === 'complete') {
220+
initAnimations();
221+
} else {
222+
window.addEventListener('load', initAnimations);
223+
}
224+
// Also try after a delay in case Mintlify hydration is still running
225+
setTimeout(function () {
226+
if (ScrollTrigger.getAll().length === 0) {
227+
initAnimations();
228+
}
229+
}, 1500);
230+
})
231+
.catch(function (e) {
232+
console.error('GSAP load failed:', e);
233+
});
234+
235+
setReady(true);
236+
}, []);
237+
238+
function initAnimations() {
239+
gsap.registerPlugin(ScrollTrigger);
240+
// Your GSAP animations here
241+
}
242+
243+
return (
244+
<div style={{
245+
opacity: ready ? 1 : 0,
246+
transition: 'opacity 0.5s ease-out'
247+
}}>
248+
{children}
249+
</div>
250+
);
251+
};
252+
```
253+
254+
The pattern has three key parts:
255+
256+
1. **Sequential loading**: Chain `.then()` calls so dependent libraries (like ScrollTrigger) load after their prerequisites (GSAP core)
257+
2. **Hydration-safe init**: Check `document.readyState` first, then also set a `setTimeout` fallback — Mintlify's hydration can delay DOM readiness beyond the browser's `load` event
258+
3. **Idempotency guard**: Before the timeout fires, check whether initialization already ran (e.g., `ScrollTrigger.getAll().length === 0`) to avoid double-initializing
259+
260+
<Warning>
261+
The `load()` function is a Mintlify global — it is **not** a standard browser API and won't work outside of Mintlify. Don't confuse it with dynamic `import()` or other module loaders.
262+
</Warning>
263+
264+
<Tip>
265+
Pin your CDN URLs to specific versions (e.g., `gsap@3.14.1`) rather than using `@latest`. Unpinned versions can break your docs without warning when the library ships a breaking change.
266+
</Tip>

0 commit comments

Comments
 (0)