<!-- Source: https://docs.squirro.com/en/latest/technical/neo/dev-guide/embedding-chat/documents.html -->
# Show Documents without the Chat

The SDK also shows Squirro documents on their own, without the chat: as a read-only documents API, as a document view, and as a search widget.

## The Documents API

`sdk.items` is the read-only documents API, through the same proxy, for pages that build their own list: `sdk.items.search({ text: 'zebrafish' }, { limit: 10 })`, `sdk.items.getItem(id)`, and `sdk.items.fetchFile(link)` for the bytes of the original file of a document.

- The `title` and `abstract` of every result and document, including the result that `onOpenItem` receives, are plain text: the search highlight tags are removed, and the character references of the abstract, such as `&amp;`, are decoded. Set them with `textContent`, never `innerHTML`, and do not decode them again.
- A failed request rejects with an `ItemsRequestError`, which carries the HTTP `status` and a `kind` that says what went wrong, such as `search-disabled` for a proxy without search. A search that the proxy refused for one of its limits also carries the `code` of the proxy, and its message names the option. On a script-tag page, check `error.name === 'ItemsRequestError'`. For every `kind`, see the `docs/chat.md` and `docs/documents.md` files in the package.

## The Document View

In React, `NeoItemDetail` renders the same document view as the overlay, anywhere inside `NeoSdkProvider`, for example in a side panel of your own:

```jsx
import { NeoItemDetail } from '@squirro/neo-sdk';

<NeoItemDetail itemId="the-squirro-item-id" pageNumber={2} onClose={() => setOpen(false)} />
```

It takes these props:

- `itemId`, the document to show.
- `pageNumber` and `highlightFragments`, as for `sdk.openDocument()`.
- `onClose`, which shows a close button. Escape does not call it.
- `className`. Size the component from its parent.

## The Search Widget

`NeoItems` adds a read-only search box and its results, and a result opens in the same document overlay. It needs `ALLOW_SEARCH=1` on the proxy, or `allowSearch: true`. Without it, the widget says that search is turned off.

For example, to search for a fixed term:

```jsx
import { NeoItems } from '@squirro/neo-sdk';

<NeoItems query="zebrafish" pageSize={10} className="h-96" />
```

It takes these props:

- `query`, which fills the box and searches. A new value searches again.
- `pageSize`, a positive integer, 15 by default.
- `onOpenItem`, which receives the result and replaces opening the overlay.
- `strings`, to replace its English text.
- `className`. Give the widget a height from its parent, since the list scrolls inside it.

To let readers narrow the results, list the facets to filter by, and the orders to offer after Relevance:

```jsx
<NeoItems
  query="zebrafish"
  filters={[{ field: 'source', label: 'Source' }, { field: 'source_type', label: 'Type' }]}
  sort={[{ label: 'Newest first', field: 'date', order: 'desc' }]}
/>
```

Each filter is a dropdown of the values of the field, with their counts: 10 by default, and up to 50 with `limit`. A widget takes at most 8 filters. Values of one field are combined with OR, and the fields with AND. `initialFilters` picks values up front, and `onFiltersChange` tells you what the reader picked. The same keys under `items` in `initNeoSdk()` set the defaults for every search widget of that instance.

Filter fields are facet names, and an order sorts by `date` or by one of the filter fields. `initialFilters` is a convenience for the reader, not access control. For the filter values that cannot match, see the `docs/documents.md` file in the package.

Because every reader shares the service account, the proxy accepts only searches like the ones that the widget sends, with at most 100 results each and a search text of at most 4,096 characters. `itemsQueryLimits` on the proxy changes these caps. Keep `pageSize` at or under the result cap.

## Without a Build Step

On a page without a build step, the same two widgets are the `<neo-items>` and `<neo-item-detail>` elements, from the same `neo-sdk.js`. They take `base-url`, `project-id`, `auth`, the theme attributes, and the theme JSON child exactly like `<neo-chat>`, and each needs a size:

```html
<script src="/assets/neo-sdk.js" defer></script>

<neo-items id="search" base-url="/api/chat" project-id="YOUR_PROJECT_ID" auth="anonymous" query="zebrafish" page-size="10"></neo-items>

<neo-item-detail
  id="report"
  base-url="/api/chat"
  project-id="YOUR_PROJECT_ID"
  auth="anonymous"
  item-id="the-squirro-item-id"
  page-number="2"
></neo-item-detail>

<style>
  neo-items,
  neo-item-detail {
    display: block;
    height: 600px;
  }
</style>

<script>
  const report = document.getElementById('report');
  report.highlightFragments = ['net revenue'];

  // Change what they show at any time; they follow their attributes.
  document.getElementById('search').setAttribute('query', 'quarterly results');
</script>
```

- `<neo-items>` takes `query` and `page-size`, and `filters` and `sort` as JSON, for example `filters='[{"field":"source","label":"Source"}]'`. Set its `onOpenItem` property to handle a click on a result yourself.
- `<neo-item-detail>` takes `item-id`, which is required, and `page-number`, a positive integer. The text to highlight is the `highlightFragments` property, since an attribute cannot hold a list.
- Changing `query`, `page-size`, `filters`, `sort`, or `item-id` later updates the widget in place. A new `page-number` applies only the next time a document loads.
- Each element is its own SDK instance. Set `pdfViewer` as a property on any of them that shows PDF documents.
- Each element reports a problem as a `neo-items-error` or `neo-item-detail-error` event, with the error in `event.detail`. A `<neo-item-detail>` without `item-id` shows nothing until you set one. After any other error, such as a missing `project-id`, fix it and add the element to the page again.
