Skip to content

Commit 93fee82

Browse files
docs(blog): add early April 2026 engineering articles
主要变更: - 添加新用户引导:使用 driver.js 实践指南 - 添加 AI 幻觉问题与 OpenSpec 规范驱动开发 - 添加 GitHub Copilot SDK 集成最佳实践 - 添加 AI 输出 token 优化:古文模式实践 - 添加渐进式披露:AI 产品人机交互改进 - 添加 Vault:AI 时代跨项目持久化存储系统 - 添加使用 Skillsbase 维护技能仓库 - 添加 Design.md:AI 驱动前端 UI 一致性解决方案 - 添加 Web 界面直接编辑 DESIGN.md 实现指南 - 添加 Vault 系统构建 AI 时代跨项目知识库 Co-Authored-By: Hagicode <[email protected]> Signed-off-by: newbe36524 <[email protected]>
1 parent 2c2aa52 commit 93fee82

10 files changed

Lines changed: 3546 additions & 0 deletions
Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
---
2+
title: "Building Elegant New User Onboarding in React Projects: HagiCode's driver.js Practice"
3+
date: 2026-04-01
4+
tags: [react, driver.js, "new user onboarding", frontend, "user experience"]
5+
---
6+
7+
## Building Elegant New User Onboarding in React Projects: HagiCode's driver.js Practice
8+
9+
> When users open your product for the first time, do they really know where to start? In this article, I want to talk a bit about how we used driver.js for new user onboarding in the HagiCode project. Consider it a small practical example to get the conversation started.
10+
11+
## Background
12+
13+
Have you ever run into this situation? A new user signs up for your product, opens the page, and immediately looks lost. They scan around, unsure what to click or what to do next. As developers, we often assume users will just "explore on their own" because, after all, human curiosity is limitless. But reality is different: most users quietly leave within minutes if they cannot find the right entry point, as if the story begins suddenly and ends just as naturally.
14+
15+
New user onboarding is an important way to solve this problem, but building it well is not that simple. A good onboarding system needs to:
16+
17+
- Precisely locate page elements and highlight them
18+
- Support multi-step onboarding flows
19+
- Remember the user's choice (complete or skip)
20+
- Avoid affecting page performance and normal interaction
21+
- Keep the code structure clear and easy to maintain
22+
23+
While building HagiCode, we ran into the same challenge. HagiCode is an AI coding assistant, and its core workflow is an OpenSpec workflow that looks like this: "the user creates a Proposal -> the AI generates a plan -> the user reviews it -> the AI executes it." For users encountering this concept for the first time, the workflow is completely new, so they need solid onboarding to get started quickly. New things always take a little time to get used to.
24+
25+
## About HagiCode
26+
27+
The approach shared in this article comes from our hands-on experience in the [HagiCode](https://hagicode.com) project. HagiCode is a Claude-based AI coding assistant that helps developers complete coding tasks more efficiently through the OpenSpec workflow. You can view our open-source code on [GitHub](https://github.com/HagiCode-org/site).
28+
29+
## Why We Chose driver.js
30+
31+
During the technical evaluation phase, we looked at several mainstream onboarding libraries. Each one had its own strengths:
32+
33+
- **Intro.js**: Powerful, but relatively large, and style customization is somewhat complex
34+
- **Shepherd.js**: Well-designed API, but a bit too "heavy" for our use case
35+
- **driver.js**: Lightweight, concise, intuitive API, and works well in the React ecosystem
36+
37+
In the end, we chose driver.js. There was no especially dramatic reason. The choice mainly came down to these considerations:
38+
39+
1. **Lightweight**: The core library is small and does not significantly increase bundle size
40+
2. **Simple API**: The configuration is clear and intuitive, so it is easy to pick up
41+
3. **Flexible**: Supports custom positioning, styling, and interaction behavior
42+
4. **Dynamic import**: Can be loaded on demand without affecting first-screen performance
43+
44+
With technology selection, there is rarely a universally best answer. Usually, there is only the option that fits best.
45+
46+
## Technical Implementation
47+
48+
### Core Configuration
49+
50+
driver.js has a very intuitive configuration model. Here is the core configuration we use in the HagiCode project:
51+
52+
```typescript
53+
import { driver } from 'driver.js';
54+
import 'driver.js/dist/driver.css';
55+
56+
const newConversationDriver = driver({
57+
allowClose: true, // Allow users to close the guide
58+
animate: true, // Enable animations
59+
overlayClickBehavior: 'close', // Close the guide when the overlay is clicked
60+
disableActiveInteraction: false, // Keep elements interactive
61+
showProgress: false, // Do not show the progress bar (we manage progress ourselves)
62+
steps: guideSteps // Array of guide steps
63+
});
64+
```
65+
66+
The reasoning behind these settings is:
67+
68+
- `allowClose: true` - Respect the user's choice and do not force them to finish the guide
69+
- `disableActiveInteraction: false` - Some steps require real user actions, such as typing input, so interaction cannot be disabled
70+
- `overlayClickBehavior: 'close'` - Give users a quick way to exit
71+
72+
### State Management
73+
74+
Persisting onboarding state is critical. We do not want to restart the guide every time the page refreshes, because that gets annoying fast. HagiCode uses `localStorage` to manage guide state:
75+
76+
```typescript
77+
export type GuideState = 'pending' | 'dismissed' | 'completed';
78+
79+
export interface UserGuideState {
80+
session: GuideState;
81+
detailGuides: Record<string, GuideState>;
82+
}
83+
84+
// Read state
85+
export const getUserGuideState = (): UserGuideState => {
86+
const state = localStorage.getItem('userGuideState');
87+
return state ? JSON.parse(state) : { session: 'pending', detailGuides: {} };
88+
};
89+
90+
// Update state
91+
export const setUserGuideState = (state: UserGuideState) => {
92+
localStorage.setItem('userGuideState', JSON.stringify(state));
93+
};
94+
```
95+
96+
We define three states:
97+
98+
- `pending`: The guide is still in progress, and the user has not completed or skipped it
99+
- `dismissed`: The user closed the guide proactively
100+
- `completed`: The user completed all steps
101+
102+
For Proposal detail page onboarding, we also support more fine-grained state tracking through the `detailGuides` map, because one Proposal can go through multiple stages - draft, review, and execution complete - and each stage needs different guidance. The state of things is always changing, and onboarding should reflect that.
103+
104+
### Target Element Selection
105+
106+
driver.js uses CSS selectors to locate target elements. HagiCode follows a simple convention: use a custom `data-guide` attribute to mark onboarding targets:
107+
108+
```typescript
109+
const steps = [
110+
{
111+
element: '[data-guide="launch"]',
112+
popover: {
113+
title: 'Start a New Conversation',
114+
description: 'Click here to create a new conversation session...'
115+
}
116+
}
117+
];
118+
```
119+
120+
In components, it looks like this:
121+
122+
```typescript
123+
<button data-guide="launch" onClick={handleLaunch}>
124+
New Conversation
125+
</button>
126+
```
127+
128+
The benefits of this approach are:
129+
130+
- Avoid conflicts with business styling class names
131+
- Clear semantics, so you can immediately tell the element is related to onboarding
132+
- Easier to manage and maintain consistently
133+
134+
### Dynamic Import Optimization
135+
136+
Because the onboarding feature is only needed in specific scenarios, such as a user's first visit, we use dynamic imports to optimize initial loading performance:
137+
138+
```typescript
139+
const initNewUserGuide = async () => {
140+
// Dynamically import driver.js
141+
const { driver } = await import('driver.js');
142+
await import('driver.js/dist/driver.css');
143+
144+
// Initialize the guide
145+
const newConversationDriver = driver({
146+
// ...configuration
147+
});
148+
149+
newConversationDriver.drive();
150+
};
151+
```
152+
153+
This way, driver.js and its stylesheet are only loaded when needed and do not affect first-screen performance. Not many people enjoy waiting for something they do not even need yet.
154+
155+
## Onboarding Flow Design
156+
157+
HagiCode implements two onboarding paths that cover the user's core scenarios.
158+
159+
### Session Onboarding (10 Steps)
160+
161+
This onboarding path helps users complete the entire flow from creating a conversation to submitting their first complete Proposal:
162+
163+
1. **launch** - Start the guide and introduce the "New Conversation" button
164+
2. **compose** - Guide the user to type a request in the input box
165+
3. **send** - Guide the user to click the send button
166+
4. **proposal-launch-readme** - Guide the user to create a README Proposal
167+
5. **proposal-compose-readme** - Guide the user to edit the README request content
168+
6. **proposal-submit-readme** - Guide the user to submit the README Proposal
169+
7. **proposal-launch-agents** - Guide the user to create an `AGENTS.md` Proposal
170+
8. **proposal-compose-agents** - Guide the user to edit the `AGENTS.md` request
171+
9. **proposal-submit-agents** - Guide the user to submit the `AGENTS.md` Proposal
172+
10. **proposal-wait** - Explain that the AI is processing and the user should wait a moment
173+
174+
The idea behind this path is to let users experience HagiCode's core workflow firsthand through two real Proposal creation tasks, one for `README` and one for `AGENTS.md`. There is a big difference between hearing about a workflow and going through it yourself.
175+
176+
The following screenshots correspond to a few key points in the session onboarding flow:
177+
178+
![Session onboarding: starting from creating a conversation session](/article-images/new-user-guide-with-driverjs/1775021925000-bbdb2431b6e4.png)
179+
180+
The first step of session onboarding takes the user to the entry point for creating a new Conversation session.
181+
182+
![Session onboarding: entering the first request](/article-images/new-user-guide-with-driverjs/1775021934000-10566759a0c6.png)
183+
184+
Next, the guide prompts the user to type their first request into the input box, lowering the barrier to getting started.
185+
186+
![Session onboarding: sending the first message](/article-images/new-user-guide-with-driverjs/1775021941000-73f42c23484d.png)
187+
188+
After the input is complete, the guide clearly prompts the user to send the first message so the action flow feels more connected.
189+
190+
![Session onboarding: waiting in the session list for continued execution](/article-images/new-user-guide-with-driverjs/1775021969000-85558db9b34d.png)
191+
192+
Once both Proposals have been created, the guide returns to the session list and lets the user know that the next step is simply to wait for the system to continue execution and refresh.
193+
194+
### Proposal Detail Onboarding (3 Steps)
195+
196+
When users enter the Proposal detail page, HagiCode triggers the corresponding guide based on the Proposal's current state:
197+
198+
1. **drafting** (draft stage) - Guide the user to review the AI-generated plan
199+
2. **reviewing** (review stage) - Guide the user to execute the plan
200+
3. **executionCompleted** (completed stage) - Guide the user to archive the plan
201+
202+
The defining characteristic of this guide is that it is state-driven: it dynamically decides which onboarding step to show based on the Proposal's actual state. Things change, and onboarding should change with them.
203+
204+
The screenshot below shows the Proposal detail page in its onboarding state during the drafting phase:
205+
206+
![Proposal detail onboarding: generate the plan first during drafting](/article-images/new-user-guide-with-driverjs/1775022103729-2cb761ccff49.png)
207+
208+
At this stage, the guide focuses the user's attention on the key action of generating a plan, so they do not wonder what to do first when entering the detail page for the first time.
209+
210+
### Element Render Retry Mechanism
211+
212+
In React applications, the target onboarding element may not have finished rendering yet, for example because asynchronous data is still loading. To handle this, HagiCode implements a retry mechanism:
213+
214+
```typescript
215+
const waitForElement = (selector: string, maxRetries = 10, interval = 100) => {
216+
let retries = 0;
217+
return new Promise<HTMLElement>((resolve, reject) => {
218+
const checkElement = () => {
219+
const element = document.querySelector(selector) as HTMLElement;
220+
if (element) {
221+
resolve(element);
222+
} else if (retries < maxRetries) {
223+
retries++;
224+
setTimeout(checkElement, interval);
225+
} else {
226+
reject(new Error(`Element not found: ${selector}`));
227+
}
228+
};
229+
checkElement();
230+
});
231+
};
232+
```
233+
234+
Call this function before initializing the guide to make sure the target element already exists. Sometimes waiting a little longer is worth it.
235+
236+
## Best Practices Summary
237+
238+
Based on HagiCode's practical experience, here are a few key best practices:
239+
240+
### 1. Onboarding Should Be Easy to Exit
241+
242+
Do not force users to complete onboarding. Some users are explorers by nature and prefer to figure things out on their own. Provide a clear "Skip" button, remember their choice, and do not interrupt them again next time.
243+
244+
### 2. Keep Onboarding Content Short and Sharp
245+
246+
Each onboarding step should focus on a single goal:
247+
248+
- **Title**: Short and clear, ideally no more than a few words
249+
- **Description**: Get straight to the point and tell the user what this is and why it matters
250+
251+
Avoid long-winded explanations. User attention is limited during onboarding, and the more you say, the less likely they are to read it.
252+
253+
### 3. Keep Selectors Stable
254+
255+
Use stable element markers that do not change often. The custom `data-guide` attribute is a good choice. Avoid depending on class names or DOM structure, because those are easy to change during refactors. Code changes all the time, but some anchors should stay stable when possible.
256+
257+
### 4. Test Your Onboarding
258+
259+
HagiCode includes complete test cases for the onboarding feature:
260+
261+
```typescript
262+
describe('NewUserConversationGuide', () => {
263+
it('should initialize guide state correctly', () => {
264+
const state = getUserGuideState();
265+
expect(state.session).toBe('pending');
266+
});
267+
268+
it('should update guide state correctly', () => {
269+
setUserGuideState({ session: 'completed', detailGuides: {} });
270+
const state = getUserGuideState();
271+
expect(state.session).toBe('completed');
272+
});
273+
});
274+
```
275+
276+
Tests help ensure that refactoring does not accidentally break onboarding behavior. Nobody wants a small code change to quietly damage previously working functionality.
277+
278+
### 5. Performance Optimization
279+
280+
- Use dynamic imports to lazy-load the onboarding library
281+
- Avoid initializing onboarding logic after the user has already completed the guide
282+
- Consider the performance impact of animations, and disable them on lower-end devices if needed
283+
284+
Performance, like many things in life, deserves a bit of careful budgeting.
285+
286+
## Conclusion
287+
288+
New user onboarding is an important part of improving product user experience. In the HagiCode project, we used driver.js to build a complete onboarding system that covers the full workflow from session creation to Proposal execution.
289+
290+
The core points we hope to share through this article are:
291+
292+
1. **Technical choices should match actual needs**: driver.js is not the most powerful option, but it is the best fit for us
293+
2. **State management is critical**: Use `localStorage` to persist onboarding state and avoid repeatedly interrupting users
294+
3. **Onboarding design should stay focused**: Each step should solve one problem, and no more
295+
4. **Code structure should stay clear**: Separate onboarding configuration, state management, and UI logic to make maintenance easier
296+
297+
If you are adding new user onboarding to your own project, I hope the practical experience in this article helps. There is nothing especially mystical about this kind of technology. Keep trying, keep summarizing what you learn, and things gradually get easier.
298+
299+
## References
300+
301+
- [driver.js official documentation](https://driverjs.com/)
302+
- [HagiCode project source code](https://github.com/HagiCode-org/site)
303+
- [HagiCode official website](https://hagicode.com)
304+
- [OpenSpec workflow documentation](https://docs.hagicode.com)
305+
306+
## Copyright Notice
307+
308+
Thank you for reading. If you found this article useful, feel free to like, bookmark, and share it.
309+
This content was created with AI-assisted collaboration, and the final content was reviewed and confirmed by the author.
310+
- Author: [newbe36524](https://www.newbe.pro)
311+
- Original article: [https://docs.hagicode.com/blog/2026-04-01-new-user-guide-with-driverjs/](https://docs.hagicode.com/blog/2026-04-01-new-user-guide-with-driverjs/)
312+
- Copyright notice: Unless otherwise stated, all articles on this blog are licensed under BY-NC-SA. Please include the source when reposting.

0 commit comments

Comments
 (0)