Skip to main content
Single-session activities are the simplest model: a student starts and completes an activity in one browser session. The client SDK tracks time automatically and reports completion when the student finishes.
For info on shared concepts, see the Custom Activities overview.

How it works

Once started, the SDK sends periodic TimeSpentEvents throughout the session. When the student finishes, the SDK sends one final TimeSpentEvent followed by an ActivityCompletedEvent. All events share the same runId for correlation.

Starting an activity

Every single-session activity starts the same way:
See the reference for full parameter documentation including time options, callbacks, and runId.

Ending an activity

How you end an activity depends on whether the student completed the activity. In both cases, accumulated time data is flushed. The difference is whether a completion result is also recorded.

With completion

When a student finishes, call activity.end() with completion metrics:
  1. Flushes a final heartbeat (TimeSpentEvent)
  2. Submits the completion (ActivityCompletedEvent)
See the reference for full completion data documentation.

Without completion

Call activity.end() without arguments to flush time data only; in this case, no completion event is recorded.
  1. Flushes a final heartbeat (TimeSpentEvent)
  2. Stops the heartbeat timer
There are two reasons to end without completion:
  1. Cleanup: the component unmounts before the student finishes (e.g. navigating away mid-activity). You want to flush accumulated time without recording a result.
  2. Stateful activities: the activity spans multiple sessions, so the client only tracks time per visit. Completion is recorded by the server when the student eventually finishes.

Framework integration

Start tracking after the browser client is initialized and the current user is verified. Use the React, Vue, Svelte, or Solid verification API. A client object alone does not establish authentication. Keep one activity owner for your lesson screen. Await end() before starting the next activity, including navigation and development-mode remounts. Async cleanup is not awaited by framework unmount hooks; starting immediately in the next mount can otherwise throw “An activity is already active.” This example is a browser event handler after initialization and verification. timeback is the initialized SDK client and showLesson is your app’s rendering function:
Serialize calls to this handler in your app. If you load lesson data asynchronously, cancel or ignore that load after the screen unmounts, before calling start(). Capture the activity created by that screen in its cleanup; end it without metrics. Handle a rejected completion in your UI and let the student retry before starting another activity.

Best practices

Start after initialization and user verification, and serialize activity transitions. Register time-only cleanup for the owning screen.
Use activity.end() (no args) in cleanup functions. Only call activity.end(metrics) when the student has actually finished the activity.
Check client initialization and user verification separately.
Use stable, unique IDs that identify the specific lesson or content piece.

Next steps

Stateful activities

Multi-session activities with server-side completion

Reference

Parameters, properties, methods, and callbacks