# Getting Started

The **Limio SDK** is a React library that gives your custom components access to everything they need — offers, basket operations, user data, subscriptions, checkout state, and more. You import hooks like `useCampaign` or `useBasket`, and the platform injects the live data at runtime.

{% hint style="info" %}
You don't need to set up API calls, authentication, or state management yourself. The SDK handles all of that behind the scenes.
{% endhint %}

***

## What can you build?

Most Limio components fall into one of these categories:

| Component type              | SDK hooks you'll use                           | Examples                                        |
| --------------------------- | ---------------------------------------------- | ----------------------------------------------- |
| **Offer / pricing cards**   | `useCampaign`, `useBasket`                     | Display plans, let users add to basket          |
| **Cart and checkout UI**    | `useBasket`, `useCheckout`                     | Show basket contents, order totals, promo codes |
| **Account pages**           | `useUser`, `useSubscriptions`                  | Display profile, active subscriptions           |
| **Billing and invoices**    | `useUserAccountInformation`, `useUserInvoices` | Show invoices, payment info, account balances   |
| **Subscription management** | `useSubscriptions`, `useBasket`                | Change plan, cancel, update add-ons             |

***

## Install the SDK

The SDK is listed as a peer dependency — Limio provides it at runtime. Add it so your editor and bundler can resolve the types:

```
npm install --save @limio/sdk

# or

yarn add @limio/sdk
```

{% hint style="warning" %}
Do **not** bundle `@limio/sdk` into your component output. It must remain an external/peer dependency so the platform can inject the correct version at runtime.
{% endhint %}

***

## Your first component — an offer card

Here's a minimal component that reads the offers configured for the current page and lets the user add one to the basket:

```tsx
import React from "react"
import { useCampaign, useBasket } from "@limio/sdk"
import { getCurrentBasketId } from "@limio/shop/src/shop/checkout/basket"

const OfferCards = () => {
  const { offers = [] } = useCampaign()
  const { initiateCheckout, addOfferToBasket, basketLoading } = useBasket()

  const handleSelect = async (offer) => {
    const checkoutId = getCurrentBasketId()

    if (!checkoutId) {
      await initiateCheckout({ order: { orderItems: [{ offer, quantity: 1 }] } })
    } else {
      await addOfferToBasket({ offer, quantity: 1 })
    }
  }

  return (
    <section>
      {offers.map((offer) => {
        const attrs = offer.data.attributes

        return (
          <div key={offer.id}>
            <h2>{attrs.display_name__limio}</h2>
            <p>{attrs.display_price__limio}</p>
            <button disabled={basketLoading} onClick={() => handleSelect(offer)}>
              {attrs.cta_text__limio || "Subscribe"}
            </button>
          </div>
        )
      })}
    </section>
  )
}

export default OfferCards
```

**What's happening here:**

1. `useCampaign()` returns the `offers` array configured for the current Limio page
2. `useBasket()` gives you the methods to add items to the basket
3. Each offer's display data lives under `offer.data.attributes` — things like `display_name__limio`, `display_price__limio`, and `cta_text__limio`
4. `initiateCheckout` creates a new basket; `addOfferToBasket` adds to an existing one
5. `basketLoading` is `true` while an async basket operation runs — use it to disable buttons and prevent double-clicks

***

## Accessing offer attributes

Every offer carries its configuration under `offer.data.attributes`. The most common fields:

| Attribute                    | Description                                                  |
| ---------------------------- | ------------------------------------------------------------ |
| `display_name__limio`        | The offer's display name                                     |
| `display_price__limio`       | Formatted price string (e.g. `"$29/mo"`)                     |
| `display_description__limio` | Short description text                                       |
| `offer_features__limio`      | Rich HTML string listing features                            |
| `cta_text__limio`            | Call-to-action button text                                   |
| `price__limio`               | Array of charge objects with `value`, `currencyCode`, `type` |
| `group__limio`               | Group label for grouping offers into tabs or sections        |

{% hint style="info" %}
Use `sanitiseHTML` from the SDK when rendering `offer_features__limio` or any rich-text attribute to prevent XSS:

```javascript
import { sanitiseHTML } from "@limio/sdk"

<div dangerouslySetInnerHTML={{ __html: sanitiseHTML(attrs.offer_features__limio) }} />
```

{% endhint %}

{% hint style="info" %}
When running locally in the Storybook playground, the SDK automatically populates hooks with sample data so you can develop without a live Limio environment.
{% endhint %}

For setting up your component repo, local Storybook development, configurable props, and deploying to Limio, see the [Custom Components](https://docs.limio.com/developers/custom-components/custom-components) guide.

***

## Common patterns

### Loading and error states

Wrap your component with `ErrorBoundary` and provide a `Skeleton` for Suspense:

```tsx
import React, { Suspense } from "react"
import { ErrorBoundary } from "@limio/sdk"

const MyComponent = () => {
  // ... your component logic
}

MyComponent.Skeleton = () => (
  <div className="skeleton-placeholder">Loading...</div>
)

const MyComponentWrapper = (props) => (
  <ErrorBoundary fallback={<p>Something went wrong.</p>}>
    <Suspense fallback={<MyComponent.Skeleton />}>
      <MyComponent {...props} />
    </Suspense>
  </ErrorBoundary>
)

export default MyComponentWrapper
```

### Page Builder detection

Use `useLimioContext` to detect when your component is rendering inside the Page Builder. This is useful for showing placeholder content during editing:

```tsx
import { useLimioContext, useCampaign } from "@limio/sdk"

const OfferCards = () => {
  const { isInPageBuilder } = useLimioContext()
  const { offers = [] } = useCampaign()

  if (isInPageBuilder && offers.length === 0) {
    return <p>Offer cards will appear here once offers are configured.</p>
  }

  return /* ... */
}
```

***

## SDK reference

For full details on each hook, see the dedicated pages:

{% content-ref url="page" %}
[page](https://docs.limio.com/developers/limio-sdk/page)
{% endcontent-ref %}

{% content-ref url="basket" %}
[basket](https://docs.limio.com/developers/limio-sdk/basket)
{% endcontent-ref %}

{% content-ref url="checkout" %}
[checkout](https://docs.limio.com/developers/limio-sdk/checkout)
{% endcontent-ref %}

{% content-ref url="user" %}
[user](https://docs.limio.com/developers/limio-sdk/user)
{% endcontent-ref %}

{% content-ref url="billing-account" %}
[billing-account](https://docs.limio.com/developers/limio-sdk/billing-account)
{% endcontent-ref %}

{% content-ref url="subscription-update-checkout" %}
[subscription-update-checkout](https://docs.limio.com/developers/limio-sdk/subscription-update-checkout)
{% endcontent-ref %}

{% content-ref url="advanced-methods" %}
[advanced-methods](https://docs.limio.com/developers/limio-sdk/advanced-methods)
{% endcontent-ref %}
