LOVABLE · BOLT · BASE44 → NATIVE FEATURES
Native app features for Lovable, Bolt and Base44: prompts to paste
Updated November 3, 2026
Before you paste a prompt
Each feature is a plugin of your SaasToStore project. The prompt changes your site; the plugin puts the feature in the app.
Add the plugin, then launch a new build
Open the plugin library of your project, add the plugin, then launch a new build: an app built before you added it doesn't have the feature. Plugins work in the native Android builds (Android Build (Native), Android TV) and in iOS apps. They are not in the Android Build (Web) (TWA), nor in the free test build, which shows your website as an app, without plugins.
Test in the app, not in the editor preview
The preview of Lovable, Bolt and Base44 shows your site in a frame of the editor, and the plugins run only in your app, never in a frame. So nothing happens there, and that's expected: the prompts keep your site working without them. Publish your site, then open your app on a phone.
Your site changes, the app follows
The app loads your published site. Once the plugin is in the app, a change you publish in Lovable, Bolt or Base44 reaches the app without a new build.
Where to paste the prompt
Each prompt stands on its own: paste one at a time, check the result, then paste the next. The AI adapts the file names to your project.
Lovable
Paste it into the chat of your project. When the change is done, click Publish: the app loads your published site, not the preview.
Bolt
Paste it into the Bolt chat, then deploy again, with Bolt's deploy button or on your own host. The app loads the deployed address.
Base44
Paste it into the AI chat of your app, then publish. The app loads your app's public address.
Haptic feedback
Short vibrations that confirm a tap, a success or an error. No permission prompt. What the person feels depends on their phone and its haptic setting; on Android, a device that can't vibrate (most tablets and TVs) plays nothing.
Prompt · haptic feedback
Add haptic feedback to my project, using the SaasToStore app bridge.
Context: my website also runs inside a native mobile app built with SaasToStore. Inside that app, `window.saastostore.haptics` makes the phone vibrate briefly. In a normal web browser, and in the editor preview, it does not exist, and the site must keep working exactly as it does today. Do not change anything else.
1. If my project has no `src/lib/saastostore.ts` yet, create it (`.js` if my project doesn't use TypeScript); if it exists, only add what is missing:
- `isInApp()`: returns `typeof window !== "undefined" && navigator.userAgent.includes("SaasToStoreApp")`.
- `bridge()`: returns `typeof window !== "undefined" ? window.saastostore : undefined`. Never read `window.saastostore` during server rendering.
- the TypeScript type of `window.saastostore` (optional), with this optional member:
`haptics?: { impact(o?: { style?: "light" | "medium" | "heavy" }): Promise<{ ok: boolean; error?: string } | null>; notification(o?: { type?: "success" | "warning" | "error" }): Promise<{ ok: boolean; error?: string } | null>; selection(): Promise<{ ok: boolean; error?: string } | null> }`
2. In the same file, add three functions that do nothing outside the app and never wait for the result:
- `tapFeedback()`: calls `bridge()?.haptics?.impact({ style: "light" })`.
- `successFeedback()`: calls `bridge()?.haptics?.notification({ type: "success" })`.
- `errorFeedback()`: calls `bridge()?.haptics?.notification({ type: "error" })`.
Their promises never reject. Ignore a result with `ok: false` (for example `not_available` on a phone that can't vibrate): never show it to the person.
3. Call `tapFeedback()` when [MY MAIN BUTTONS] are tapped, `successFeedback()` when [A FORM, FOR EXAMPLE THE CHECKOUT] succeeds and `errorFeedback()` when it fails.
4. Don't vibrate on scroll, on every key typed, or several times in a row.Replace the values in [BRACKETS] before pasting. Add the plugin in your project, then launch a new build.
QR code scanner
A Scan button that reads a QR code or a barcode with the camera: a ticket, a product, a link. On Android, the reader is Google's (Google Play services), and your app asks for no camera permission; phones without Google Play services, such as Amazon Fire and Huawei, don't have it. On iOS, the app opens its own reader and asks for the camera the first time. Try it with a real code on a real phone before you publish.
Prompt · QR code scanner
Add a QR code scanner to my project, using the SaasToStore app bridge.
Context: my website also runs inside a native mobile app built with SaasToStore. Inside that app, `window.saastostore.barcode` opens the phone's code reader. In a normal web browser, and in the editor preview, it does not exist, and the site must keep working exactly as it does today. Do not change anything else.
1. If my project has no `src/lib/saastostore.ts` yet, create it (`.js` if my project doesn't use TypeScript); if it exists, only add what is missing:
- `isInApp()`: returns `typeof window !== "undefined" && navigator.userAgent.includes("SaasToStoreApp")`.
- `bridge()`: returns `typeof window !== "undefined" ? window.saastostore : undefined`. Never read `window.saastostore` during server rendering.
- the TypeScript type of `window.saastostore` (optional), with this optional member:
`barcode?: { isAvailable(): Promise<{ available: boolean; reason?: string } | null>; scan(o?: { formats?: string[]; prompt?: string }): Promise<{ scanned: true; value: string; format: string } | { scanned: false; error: string } | null> }`
2. On [THE PAGE WHERE PEOPLE SCAN], show a "Scan a code" button only when `isInApp()` is true and `bridge()?.barcode?.isAvailable()` answers `available: true`. Otherwise keep the page as it is today (for example a field to type the code).
3. When the button is tapped, call `bridge().barcode.scan({ formats: ["qr"], prompt: "[TEXT ABOVE THE VIEWFINDER, 200 CHARACTERS MAX]" })`. Its promise never rejects:
- `scanned: true`: use `value` to [WHAT TO DO WITH THE CODE, FOR EXAMPLE OPEN THE TICKET].
- `error: "cancelled"`: do nothing, the person closed the reader.
- `error: "not_allowed"` (iOS, camera refused): explain that the camera can be allowed in the phone's settings.
- any other error: show a short message.
4. `value` is text from an unknown source: check it before using it, never insert it into the page as HTML, and never open it as a link without checking that it points to [MY DOMAIN].Replace the values in [BRACKETS] before pasting. Add the plugin in your project, then launch a new build.
Local notifications (reminders)
A Remind me button that schedules a notification on the phone, shown at the chosen time even when the app is closed: an appointment, a session that starts, a cart left behind. No server is involved: this is not push, and it can't reach all your users at once. Android shows the reminder within a few minutes of the time; iOS is on time. Reminders survive a restart of the phone. Limits: 64 pending reminders, up to 366 days ahead.
Prompt · local notifications
Add reminders (local notifications) to my project, using the SaasToStore app bridge.
Context: my website also runs inside a native mobile app built with SaasToStore. Inside that app, `window.saastostore.localNotifications` schedules notifications on the phone itself, with no server. In a normal web browser, and in the editor preview, it does not exist, and the site must keep working exactly as it does today. Do not change anything else.
1. If my project has no `src/lib/saastostore.ts` yet, create it (`.js` if my project doesn't use TypeScript); if it exists, only add what is missing:
- `isInApp()`: returns `typeof window !== "undefined" && navigator.userAgent.includes("SaasToStoreApp")`.
- `bridge()`: returns `typeof window !== "undefined" ? window.saastostore : undefined`. Never read `window.saastostore` during server rendering.
- the TypeScript type of `window.saastostore` (optional), with this optional member:
`localNotifications?: { requestPermission(): Promise<{ granted: boolean; status: "granted" | "denied" | "prompt"; error?: string } | null>; schedule(o: { id?: number; title: string; body?: string; at?: number | string; in?: number; data?: object }): Promise<{ scheduled: true; id: number; at: number } | { scheduled: false; error: string; status?: string } | null>; cancel(o: { id: number }): Promise<{ ok: boolean; error?: string } | null>; getLastOpened(): Promise<{ notification: { id: number; title: string; body: string; at: number; data: object } | null } | null> }`
2. On [THE ITEM THAT DESERVES A REMINDER, FOR EXAMPLE A BOOKING], show a "Remind me" button only when `isInApp()` is true and `bridge()?.localNotifications` exists.
3. When the button is tapped (never when the page loads):
- first call `bridge().localNotifications.requestPermission()`. If `granted` is false, explain that notifications can be allowed in the phone's settings, and stop.
- then call `bridge().localNotifications.schedule({ id: [A NUMBER UNIQUE TO THIS ITEM], title: [TITLE, 200 CHARACTERS MAX], body: [SHORT TEXT], at: [THE TIME, AS A TIMESTAMP IN MILLISECONDS OR date.toISOString()], data: { [THE ITEM ID] } })`. Show "Reminder set" when `scheduled` is true, and a short message otherwise.
These promises never reject.
4. When the person taps the notification, open [THE PAGE OF THE ITEM] with the id found in `data`:
- listen to `window.addEventListener("s2s:localNotifications", (e) => …)` and act when `e.detail.type === "tap"`, using `e.detail.data`;
- also call `bridge()?.localNotifications?.getLastOpened()` once when the app starts: if the app was closed, the event fired before the page was listening. It returns `{ notification }` once, then `{ notification: null }`.
5. When the item is cancelled or the reminder is no longer useful, call `bridge()?.localNotifications?.cancel({ id })`.Replace the values in [BRACKETS] before pasting. Add the plugin in your project, then launch a new build.
Check the result
- In a normal browser: the page looks and works as before, with no button that does nothing.
- In your app, built after you added the plugin: the button shows and does its job.
- Close the share sheet or the scanner without choosing anything: no error message appears.
- Refuse the permission once (notifications; the camera on iOS): the page says what to do instead of failing silently.
Frequently asked questions
Does the same prompt work in Lovable, Bolt and Base44?+
Yes. Each prompt describes the change, not a tool, so the same text works in the chat of Lovable, Bolt and Base44, and in other AI builders that edit your site's code. The AI picks the right file names for your project.
Why does nothing happen in the editor preview?+
The preview shows your site in a frame of the editor. The plugins run only in the main frame of your app, never in a frame or in a browser, so window.saastostore does not exist there. The prompts keep your site working without it. To try a feature, publish your site and open your app on a phone.
Do I need a new build after pasting a prompt?+
Only to add the plugin. Plugins are written into the app when it's built: add the plugin in your project's plugin library, then launch a new build. After that, a change you publish on your site reaches the app without a new build, because the app loads your published site.
Are these plugins included in my plan?+
Standard plugins are included in the paid plans, with no license per plugin: add as many as your app needs. The free test build (Android, with the SaasToStore logo) shows your website as an app, without plugins.
Will my app ask for new permissions?+
Native share and haptic feedback ask for nothing. The QR scanner asks for the camera on iOS, the first time it scans; on Android it uses Google's reader and asks for nothing. Local notifications ask for permission on Android 13 and later and on iOS, when your site calls requestPermission() from a button.
Can I send a notification to all my users?+
Not with these prompts. A local notification is scheduled by your page on one phone, with no server. A message sent from your server to your users is push, a different plugin.
Related guides
Add the feature to your app
Paste your site's URL, add the plugins in your project, then build. Your site keeps working in the browser.
Start my app