[Canonical page](<https://docs.dhtmlx.com/planning-engine/js/api/project-preview/>)
[Full documentation index](<https://docs.dhtmlx.com/planning-engine/llms.txt>)


# Project#preview

Applies hypothetical changes to a copy of the solved baseline of a [`Project`](<https://docs.dhtmlx.com/planning-engine/js/api/project.md>) and schedules the copy. The project isn't modified. Use it to show what an edit would do before committing it with [`apply`](<https://docs.dhtmlx.com/planning-engine/js/api/project-apply.md>). Needs a solved project.

## Signature

```ts
preview(changes: SchedulingChanges, options?: PreviewOptions): ScheduleResult
```

Synchronous.

## Parameters

| Name | Type | Description |
|---|---|---|
| `changes` | [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/js/api/types/scheduling-changes.md>) | The edits to try |
| `options` | [`PreviewOptions`](<https://docs.dhtmlx.com/planning-engine/js/api/project-preview.md#previewoptions>) | Optional. Subtree, analytics, reasons, baseline check and load profile. Default `{}` |

### PreviewOptions

Options for one `preview` call. Every field is optional.

| Field | Type | Default | Description |
|---|---|---|---|
| `subtree` | [`Subtree`](<https://docs.dhtmlx.com/planning-engine/js/api/types/common.md#subtree>) | whole project | Schedules only one task's subtree and its successors |
| `analytics` | [`AnalyticsOptions`](<https://docs.dhtmlx.com/planning-engine/js/api/types/common.md#analyticsoptions>) | none | Analytics to compute for the previewed dates. Fills the result's `analytics` |
| `reasons` | `boolean` | `false` | `true` adds `reason` to each entry of the result's `updates` |
| `baselineId` | `string` | no check | Expected baseline token, taken from the latest result's `baselineId`. A mismatch throws `ProjectError` `"stale-baseline"` |
| `profile` | [`ProfileQuery`](<https://docs.dhtmlx.com/planning-engine/js/api/types/common.md#profilequery>) | no profile | Fills the result's `profile` with a [`LoadProfile`](<https://docs.dhtmlx.com/planning-engine/js/api/types/load-profile.md>) of the previewed dates. An invalid query throws `InputError` |

`PreviewOptions` has the same `subtree`, `analytics` and `reasons` fields as [`SolveOptions`](<https://docs.dhtmlx.com/planning-engine/js/api/types/solve-options.md>), but no `includeUnchanged`: preview results are always sparse.

## Behavior

- `updates` are relative to the baseline. An empty change set gives empty `updates`.
- The result's `baselineId` is the baseline the preview read, so a following `preview` accepts it.
- The change set isn't partially accepted: one rejected operation fails the whole call.
- `options.baselineId` and `changes.baselineId` are checked separately. An absent token skips its check.
- `changes` and `options` are converted like every other input; see [Plain objects and dates](<https://docs.dhtmlx.com/planning-engine/js/plain-objects-and-dates.md>).

:::note
A start date that falls on non-working time is kept unsnapped when the task has no predecessor. The end is computed forward from it on the working calendar.
:::

## Returns

A [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/js/api/types/schedule-result.md>) for the previewed state. With `dates: "date"` on the project, its dates are local `Date` objects.

## Errors

| Error | When |
|---|---|
| [`ProjectError`](<https://docs.dhtmlx.com/planning-engine/js/api/types/errors.md>) `"no-baseline"` | The project is unsolved, including after `apply` |
| `ProjectError` `"stale-baseline"` | `options.baselineId` or `changes.baselineId` doesn't name the current baseline |
| [`InputError`](<https://docs.dhtmlx.com/planning-engine/js/api/types/errors.md>) | An operation in `changes` is rejected (the error reports the first one, with its `path`), the `profile` query is invalid, or the input is malformed |
| `Error("Project has been disposed")` | The project was disposed |

## Example

Preview a drag of task `b` and check that the project keeps its dates.

```js
import { Project } from "plen-js";

const project = new Project();
const solved = project.solve({
  config: { use_calendars: false },
  tasks: [{ id: "b", start_date: "2026-01-01", duration: 1 }],
});

const preview = project.preview(
  { tasks: { update: [{ id: "b", start_date: "2026-01-05" }] } },
  { baselineId: solved.baselineId },
);
for (const update of preview.updates) {
  console.log(`task ${update.id}: ${update.start_date} .. ${update.end_date}`);
  // task b: 2026-01-05 00:00 .. 2026-01-06 00:00
}

// The project is unchanged: an empty preview reports no updates.
const unchanged = project.preview({});
console.log(unchanged.updates.length); // 0
```

## Related

- [`Project`](<https://docs.dhtmlx.com/planning-engine/js/api/project.md>) - the class and its other methods
- [`SchedulingChanges`](<https://docs.dhtmlx.com/planning-engine/js/api/types/scheduling-changes.md>) - the change set
- [`ScheduleResult`](<https://docs.dhtmlx.com/planning-engine/js/api/types/schedule-result.md>) - the returned result
- [Errors](<https://docs.dhtmlx.com/planning-engine/js/api/types/errors.md#projecterror>) - `no-baseline` and `stale-baseline`
- [Previewing Changes](<https://docs.dhtmlx.com/planning-engine/js/live-projects/previewing-changes.md>) - previews, baseline tokens and clones
