diff --git a/.size-limit.js b/.size-limit.js
index 5e94a923e656..cbc2f411b628 100644
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -61,6 +61,27 @@ module.exports = [
gzip: true,
limit: '50 KB',
},
+ {
+ name: '@sentry/browser (incl. feedback2Integration) - Webpack (gzipped)',
+ path: 'packages/browser/build/npm/esm/index.js',
+ import: '{ init, feedback2Integration }',
+ gzip: true,
+ limit: '50 KB',
+ },
+ {
+ name: '@sentry/browser (incl. feedback2ModalIntegration) - Webpack (gzipped)',
+ path: 'packages/browser/build/npm/esm/index.js',
+ import: '{ init, feedback2Integration, feedback2ModalIntegration }',
+ gzip: true,
+ limit: '50 KB',
+ },
+ {
+ name: '@sentry/browser (incl. feedback2ScreenshotIntegration) - Webpack (gzipped)',
+ path: 'packages/browser/build/npm/esm/index.js',
+ import: '{ init, feedback2Integration, feedback2ModalIntegration, feedback2ScreenshotIntegration }',
+ gzip: true,
+ limit: '50 KB',
+ },
{
name: '@sentry/browser (incl. sendFeedback) - Webpack (gzipped)',
path: 'packages/browser/build/npm/esm/index.js',
diff --git a/package.json b/package.json
index ef36567d47e3..414d4d00713b 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"packages/eslint-plugin-sdk",
"packages/feedback",
"packages/feedback-screenshot",
+ "packages/feedback2",
"packages/gatsby",
"packages/hub",
"packages/integrations",
diff --git a/packages/feedback2/.eslintignore b/packages/feedback2/.eslintignore
new file mode 100644
index 000000000000..b38db2f296ff
--- /dev/null
+++ b/packages/feedback2/.eslintignore
@@ -0,0 +1,2 @@
+node_modules/
+build/
diff --git a/packages/feedback2/.eslintrc.js b/packages/feedback2/.eslintrc.js
new file mode 100644
index 000000000000..ef2ed992265c
--- /dev/null
+++ b/packages/feedback2/.eslintrc.js
@@ -0,0 +1,25 @@
+// Note: All paths are relative to the directory in which eslint is being run, rather than the directory where this file
+// lives
+
+// ESLint config docs: https://eslint.org/docs/user-guide/configuring/
+
+module.exports = {
+ extends: ['../../.eslintrc.js'],
+ overrides: [
+ {
+ files: ['src/**/*.ts', 'src/**/*.tsx'],
+ rules: {
+ '@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
+ },
+ },
+ {
+ files: ['jest.setup.ts', 'jest.config.ts'],
+ parserOptions: {
+ project: ['tsconfig.test.json'],
+ },
+ rules: {
+ 'no-console': 'off',
+ },
+ },
+ ],
+};
diff --git a/packages/feedback2/.gitignore b/packages/feedback2/.gitignore
new file mode 100644
index 000000000000..363d3467c6fa
--- /dev/null
+++ b/packages/feedback2/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+/*.tgz
+.eslintcache
+build
diff --git a/packages/feedback2/LICENSE b/packages/feedback2/LICENSE
new file mode 100644
index 000000000000..d11896ba1181
--- /dev/null
+++ b/packages/feedback2/LICENSE
@@ -0,0 +1,14 @@
+Copyright (c) 2023 Sentry (https://sentry.io) and individual contributors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
+persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/feedback2/README.md b/packages/feedback2/README.md
new file mode 100644
index 000000000000..fb5b20400a71
--- /dev/null
+++ b/packages/feedback2/README.md
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+# Sentry Integration for Feedback
+
+This SDK is **considered experimental and in a beta state**. It may experience breaking changes, and may be discontinued at any time. Please reach out on
+[GitHub](https://github.com/getsentry/sentry-javascript/issues/new/choose) if you have any feedback/concerns.
+
+To view Feedback in Sentry, your [Sentry organization must be an early adopter](https://docs.sentry.io/product/accounts/early-adopter-features/).
+
+## Installation
+
+Please read the [offical integration documentation](https://docs.sentry.io/platforms/javascript/user-feedback/) for installation instructions.
+
+## Configuration
+
+The Feedback integration is highly customizable, please read the [official integration documentation](https://docs.sentry.io/platforms/javascript/user-feedback/configuration/) for the most up-to-date configuration options.
diff --git a/packages/feedback2/jest.config.js b/packages/feedback2/jest.config.js
new file mode 100644
index 000000000000..cd02790794a7
--- /dev/null
+++ b/packages/feedback2/jest.config.js
@@ -0,0 +1,6 @@
+const baseConfig = require('../../jest/jest.config.js');
+
+module.exports = {
+ ...baseConfig,
+ testEnvironment: 'jsdom',
+};
diff --git a/packages/feedback2/package.json b/packages/feedback2/package.json
new file mode 100644
index 000000000000..416ba4436049
--- /dev/null
+++ b/packages/feedback2/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "@sentry-internal/feedback2",
+ "version": "7.100.0",
+ "description": "Sentry SDK integration for user feedback",
+ "repository": "git://github.com/getsentry/sentry-javascript.git",
+ "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/feedback",
+ "author": "Sentry",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "files": [
+ "cjs",
+ "esm",
+ "types",
+ "types-ts3.8"
+ ],
+ "main": "build/npm/cjs/index.js",
+ "module": "build/npm/esm/index.js",
+ "types": "build/npm/types/index.d.ts",
+ "typesVersions": {
+ "<4.9": {
+ "build/npm/types/index.d.ts": [
+ "build/npm/types-ts3.8/index.d.ts"
+ ]
+ }
+ },
+ "publishConfig": {
+ "access": "public"
+ },
+ "dependencies": {
+ "@sentry/core": "7.100.0",
+ "@sentry/types": "7.100.0",
+ "@sentry/utils": "7.100.0",
+ "preact": "^10.19.4",
+ "preact-compat": "^3.19.0"
+ },
+ "scripts": {
+ "build": "run-p build:transpile build:types build:bundle",
+ "build:transpile": "rollup -c rollup.npm.config.mjs",
+ "build:bundle": "rollup -c rollup.bundle.config.mjs",
+ "build:dev": "run-p build:transpile build:types",
+ "build:types": "run-s build:types:core build:types:downlevel",
+ "build:types:core": "tsc -p tsconfig.types.json",
+ "build:types:downlevel": "yarn downlevel-dts build/npm/types build/npm/types-ts3.8 --to ts3.8",
+ "build:watch": "run-p build:transpile:watch build:bundle:watch build:types:watch",
+ "build:dev:watch": "run-p build:transpile:watch build:types:watch",
+ "build:transpile:watch": "yarn build:transpile --watch",
+ "build:bundle:watch": "yarn build:bundle --watch",
+ "build:types:watch": "tsc -p tsconfig.types.json --watch",
+ "build:tarball": "ts-node ../../scripts/prepack.ts --bundles && npm pack ./build/npm",
+ "circularDepCheck": "madge --circular src/index.ts",
+ "clean": "rimraf build sentry-feedback-*.tgz",
+ "fix": "eslint . --format stylish --fix",
+ "lint": "eslint . --format stylish",
+ "test": "jest",
+ "test:watch": "jest --watch",
+ "yalc:publish": "ts-node ../../scripts/prepack.ts --bundles && yalc publish ./build/npm --push --sig"
+ },
+ "volta": {
+ "extends": "../../package.json"
+ },
+ "sideEffects": false
+}
diff --git a/packages/feedback2/rollup.bundle.config.mjs b/packages/feedback2/rollup.bundle.config.mjs
new file mode 100644
index 000000000000..32c9684415bf
--- /dev/null
+++ b/packages/feedback2/rollup.bundle.config.mjs
@@ -0,0 +1,15 @@
+import { makeBaseBundleConfig, makeBundleConfigVariants } from '@sentry-internal/rollup-utils';
+
+export default makeBundleConfigVariants(
+ makeBaseBundleConfig({
+ bundleType: 'addon',
+ entrypoints: ['src/index.ts'],
+ jsVersion: 'es6',
+ licenseTitle: '@sentry-internal/feedback2',
+ outputFileBase: () => 'bundles/feedback2',
+ sucrase: {
+ jsxPragma: 'h',
+ jsxFragmentPragma: 'Fragment',
+ },
+ }),
+);
diff --git a/packages/feedback2/rollup.npm.config.mjs b/packages/feedback2/rollup.npm.config.mjs
new file mode 100644
index 000000000000..80f2406e78db
--- /dev/null
+++ b/packages/feedback2/rollup.npm.config.mjs
@@ -0,0 +1,20 @@
+import { makeBaseNPMConfig, makeNPMConfigVariants } from '@sentry-internal/rollup-utils';
+
+export default makeNPMConfigVariants(
+ makeBaseNPMConfig({
+ hasBundles: true,
+ packageSpecificConfig: {
+ output: {
+ // set exports to 'named' or 'auto' so that rollup doesn't warn
+ exports: 'named',
+ // set preserveModules to false because for Feedback we actually want
+ // to bundle everything into one file.
+ preserveModules: false,
+ },
+ },
+ sucrase: {
+ jsxPragma: 'h',
+ jsxFragmentPragma: 'Fragment',
+ },
+ }),
+);
diff --git a/packages/feedback2/src/constants/index.ts b/packages/feedback2/src/constants/index.ts
new file mode 100644
index 000000000000..9804fdedf431
--- /dev/null
+++ b/packages/feedback2/src/constants/index.ts
@@ -0,0 +1,27 @@
+import { GLOBAL_OBJ } from '@sentry/utils';
+
+export { DEFAULT_THEME } from './theme';
+
+// exporting a separate copy of `WINDOW` rather than exporting the one from `@sentry/browser`
+// prevents the browser package from being bundled in the CDN bundle, and avoids a
+// circular dependency between the browser and feedback packages
+export const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window;
+export const DOCUMENT = WINDOW.document;
+export const NAVIGATOR = WINDOW.navigator;
+
+export const ACTOR_LABEL = 'Report a Bug';
+export const CANCEL_BUTTON_LABEL = 'Cancel';
+export const SUBMIT_BUTTON_LABEL = 'Send Bug Report';
+export const FORM_TITLE = 'Report a Bug';
+export const EMAIL_PLACEHOLDER = 'your.email@example.org';
+export const EMAIL_LABEL = 'Email';
+export const MESSAGE_PLACEHOLDER = "What's the bug? What did you expect?";
+export const MESSAGE_LABEL = 'Description';
+export const NAME_PLACEHOLDER = 'Your Name';
+export const NAME_LABEL = 'Name';
+export const SUCCESS_MESSAGE_TEXT = 'Thank you for your report!';
+
+export const FEEDBACK_WIDGET_SOURCE = 'widget';
+export const FEEDBACK_API_SOURCE = 'api';
+
+export const SUCCESS_MESSAGE_TIMEOUT = 5000;
diff --git a/packages/feedback2/src/constants/theme.ts b/packages/feedback2/src/constants/theme.ts
new file mode 100644
index 000000000000..7fff31f48964
--- /dev/null
+++ b/packages/feedback2/src/constants/theme.ts
@@ -0,0 +1,55 @@
+const LIGHT_BACKGROUND = '#ffffff';
+const INHERIT = 'inherit';
+const SUBMIT_COLOR = 'rgba(108, 95, 199, 1)';
+
+export const LIGHT_THEME = {
+ fontFamily: "system-ui, 'Helvetica Neue', Arial, sans-serif",
+ fontSize: '14px',
+
+ background: LIGHT_BACKGROUND,
+ backgroundHover: '#f6f6f7',
+ foreground: '#2b2233',
+ border: '1.5px solid rgba(41, 35, 47, 0.13)',
+ borderRadius: '12px',
+ boxShadow: '0px 4px 24px 0px rgba(43, 34, 51, 0.12)',
+
+ success: '#268d75',
+ error: '#df3338',
+
+ submitBackground: 'rgba(88, 74, 192, 1)',
+ submitBackgroundHover: SUBMIT_COLOR,
+ submitBorder: SUBMIT_COLOR,
+ submitOutlineFocus: '#29232f',
+ submitForeground: LIGHT_BACKGROUND,
+ submitForegroundHover: LIGHT_BACKGROUND,
+
+ cancelBackground: 'transparent',
+ cancelBackgroundHover: 'var(--background-hover)',
+ cancelBorder: 'var(--border)',
+ cancelOutlineFocus: 'var(--input-outline-focus)',
+ cancelForeground: 'var(--foreground)',
+ cancelForegroundHover: 'var(--foreground)',
+
+ inputBackground: INHERIT,
+ inputForeground: INHERIT,
+ inputBorder: 'var(--border)',
+ inputOutlineFocus: SUBMIT_COLOR,
+
+ formBorderRadius: '20px',
+ formContentBorderRadius: '6px',
+};
+
+export const DEFAULT_THEME = {
+ light: LIGHT_THEME,
+ dark: {
+ ...LIGHT_THEME,
+
+ background: '#29232f',
+ backgroundHover: '#352f3b',
+ foreground: '#ebe6ef',
+ border: '1.5px solid rgba(235, 230, 239, 0.15)',
+
+ success: '#2da98c',
+ error: '#f55459',
+ },
+};
diff --git a/packages/feedback2/src/core/components/Actor.css.ts b/packages/feedback2/src/core/components/Actor.css.ts
new file mode 100644
index 000000000000..9ae8df503cc9
--- /dev/null
+++ b/packages/feedback2/src/core/components/Actor.css.ts
@@ -0,0 +1,63 @@
+import { DOCUMENT } from '../../constants';
+
+/**
+ * Creates