diff --git a/pages/docs/manual/v12.0.0/async-await.mdx b/pages/docs/manual/v12.0.0/async-await.mdx
index ebe40b49c..1a459ce06 100644
--- a/pages/docs/manual/v12.0.0/async-await.mdx
+++ b/pages/docs/manual/v12.0.0/async-await.mdx
@@ -130,15 +130,15 @@ You may use `try / catch` or `switch` to handle exceptions during async executio
 ```res
 // For simulation purposes
 let authenticate = async () => {
-  raise(Exn.raiseRangeError("Authentication failed."))
+  JsError.RangeError.throwWithMessage("Authentication failed.")
 }
 
 let checkAuth = async () => {
   try {
     await authenticate()
   } catch {
-  | Exn.Error(e) =>
-    switch Exn.message(e) {
+  | JsExn(e) =>
+    switch JsExn.message(e) {
     | Some(msg) => Console.log("JS error thrown: " ++ msg)
     | None => Console.log("Some other exception has been thrown")
     }
@@ -152,14 +152,14 @@ You may unify error and value handling in a single switch as well:
 
 ```res
 let authenticate = async () => {
-  raise(Exn.raiseRangeError("Authentication failed."))
+  JsError.RangeError.throwWithMessage("Authentication failed.")
 }
 
 let checkAuth = async () => {
   switch await authenticate() {
   | _ => Console.log("ok")
-  | exception Exn.Error(e) => 
-    switch Exn.message(e) {
+  | exception JsExn(e) => 
+    switch JsExn.message(e) {
     | Some(msg) => Console.log("JS error thrown: " ++ msg)
     | None => Console.log("Some other exception has been thrown")
     }
diff --git a/pages/docs/manual/v12.0.0/exception.mdx b/pages/docs/manual/v12.0.0/exception.mdx
index 1fb7facb4..176c48c9e 100644
--- a/pages/docs/manual/v12.0.0/exception.mdx
+++ b/pages/docs/manual/v12.0.0/exception.mdx
@@ -15,18 +15,18 @@ You can create your own exceptions like you'd make a variant (exceptions need to
 ```res example
 exception InputClosed(string)
 // later on
-raise(InputClosed("The stream has closed!"))
+throw(InputClosed("The stream has closed!"))
 ```
 ```js
-import * as Caml_exceptions from "./stdlib/caml_exceptions.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
-var InputClosed = /* @__PURE__ */Caml_exceptions.create("Playground.InputClosed");
+let InputClosed = /* @__PURE__ */Primitive_exceptions.create("Playground.InputClosed");
 
 throw {
-      RE_EXN_ID: InputClosed,
-      _1: "The stream has closed!",
-      Error: new Error()
-    };
+  RE_EXN_ID: InputClosed,
+  _1: "The stream has closed!",
+  Error: new Error()
+};
 ```
 
 </CodeTab>
@@ -45,7 +45,7 @@ let getItem = (item: int) =>
     // return the found item here
     1
   } else {
-    raise(Not_found)
+    throw(Not_found)
   }
 
 let result =
@@ -56,25 +56,24 @@ let result =
   }
 ```
 ```js
-import * as Caml_js_exceptions from "./stdlib/caml_js_exceptions.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
 function getItem(item) {
   if (item === 3) {
     return 1;
   }
   throw {
-        RE_EXN_ID: "Not_found",
-        Error: new Error()
-      };
+    RE_EXN_ID: "Not_found",
+    Error: new Error()
+  };
 }
 
-var result;
+let result;
 
 try {
   result = getItem(2);
-}
-catch (raw_exn){
-  var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
+} catch (raw_exn) {
+  let exn = Primitive_exceptions.internalToException(raw_exn);
   if (exn.RE_EXN_ID === "Not_found") {
     result = 0;
   } else {
@@ -98,28 +97,27 @@ switch list{1, 2, 3}->List.getExn(4) {
 }
 ```
 ```js
-import * as Core__List from "./stdlib/core__List.js";
-import * as Caml_js_exceptions from "./stdlib/caml_js_exceptions.js";
+import * as Stdlib_List from "./stdlib/Stdlib_List.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
-var exit = 0;
+let exit = 0;
 
-var item;
+let item;
 
 try {
-  item = Core__List.getExn({
-        hd: 1,
-        tl: {
-          hd: 2,
-          tl: {
-            hd: 3,
-            tl: /* [] */0
-          }
-        }
-      }, 4);
+  item = Stdlib_List.getExn({
+    hd: 1,
+    tl: {
+      hd: 2,
+      tl: {
+        hd: 3,
+        tl: /* [] */0
+      }
+    }
+  }, 4);
   exit = 1;
-}
-catch (raw_exn){
-  var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
+} catch (raw_exn) {
+  let exn = Primitive_exceptions.internalToException(raw_exn);
   if (exn.RE_EXN_ID === "Not_found") {
     console.log("No such item found!");
   } else {
@@ -142,7 +140,7 @@ Used to check if argument is valid. This exception takes a string.
 ```res example
 let divide = (a, b) =>
   if b == 0 {
-    raise(Invalid_argument("Denominator is zero"))
+    throw(Invalid_argument("Denominator is zero"))
   } else {
     a / b
   }
@@ -154,25 +152,24 @@ try divide(2, 0)->Console.log catch {
 ```
 
 ```js
-import * as Caml_int32 from "./stdlib/caml_int32.js";
-import * as Caml_js_exceptions from "./stdlib/caml_js_exceptions.js";
+import * as Primitive_int from "./stdlib/Primitive_int.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
 function divide(a, b) {
   if (b === 0) {
     throw {
-          RE_EXN_ID: "Invalid_argument",
-          _1: "Denominator is zero",
-          Error: new Error()
-        };
+      RE_EXN_ID: "Invalid_argument",
+      _1: "Denominator is zero",
+      Error: new Error()
+    };
   }
-  return Caml_int32.div(a, b);
+  return Primitive_int.div(a, b);
 }
 
 try {
   console.log(divide(2, 0));
-}
-catch (raw_msg){
-  var msg = Caml_js_exceptions.internalToOCamlException(raw_msg);
+} catch (raw_msg) {
+  let msg = Primitive_exceptions.internalToException(raw_msg);
   if (msg.RE_EXN_ID === "Invalid_argument") {
     console.log(msg._1);
   } else {
@@ -185,7 +182,7 @@ catch (raw_msg){
 
 ### `Assert_failure`
 
-Raise when you use `assert(condition)` and `condition` is false. The arguments
+Thrown when you use `assert(condition)` and `condition` is false. The arguments
 are the location of the `assert` in the source code (file name, line number, column number).
 
 <CodeTab labels={["ReScript", "JS Output"]}>
@@ -208,55 +205,43 @@ try decodeUser(%raw("{}"))->Console.log catch {
 ```
 
 ```js
-mport * as Caml_js_exceptions from "./stdlib/caml_js_exceptions.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
 function decodeUser(json) {
-  if (!Array.isArray(json) && (json === null || typeof json !== "object") && typeof json !== "number" && typeof json !== "string" && typeof json !== "boolean") {
-    throw {
-          RE_EXN_ID: "Assert_failure",
-          _1: [
-            "playground.res",
-            8,
-            9
-          ],
-          Error: new Error()
-        };
-  }
-  if (typeof json === "object" && !Array.isArray(json)) {
-    var match = json["name"];
-    var match$1 = json["age"];
-    if (match !== undefined && !(!Array.isArray(match) && (match === null || typeof match !== "object") && typeof match !== "number" && typeof match !== "string" && typeof match !== "boolean") && typeof match === "string" && match$1 !== undefined && !(!Array.isArray(match$1) && (match$1 === null || typeof match$1 !== "object") && typeof match$1 !== "number" && typeof match$1 !== "string" && typeof match$1 !== "boolean") && typeof match$1 === "number") {
+  if (typeof json === "object" && json !== null && !Array.isArray(json)) {
+    let match = json["name"];
+    let match$1 = json["age"];
+    if (typeof match === "string" && typeof match$1 === "number") {
       return [
-              match,
-              match$1 | 0
-            ];
+        match,
+        match$1 | 0
+      ];
     }
     throw {
-          RE_EXN_ID: "Assert_failure",
-          _1: [
-            "playground.res",
-            6,
-            11
-          ],
-          Error: new Error()
-        };
+      RE_EXN_ID: "Assert_failure",
+      _1: [
+        "playground.res",
+        6,
+        11
+      ],
+      Error: new Error()
+    };
   }
   throw {
-        RE_EXN_ID: "Assert_failure",
-        _1: [
-          "playground.res",
-          8,
-          9
-        ],
-        Error: new Error()
-      };
+    RE_EXN_ID: "Assert_failure",
+    _1: [
+      "playground.res",
+      8,
+      9
+    ],
+    Error: new Error()
+  };
 }
 
 try {
   console.log(decodeUser({}));
-}
-catch (raw_loc){
-  var loc = Caml_js_exceptions.internalToOCamlException(raw_loc);
+} catch (raw_loc) {
+  let loc = Primitive_exceptions.internalToException(raw_loc);
   if (loc.RE_EXN_ID === "Assert_failure") {
     console.log(loc._1);
   } else {
@@ -269,7 +254,7 @@ catch (raw_loc){
 
 ### `Failure`
 
-Exception raised to signal that the given arguments do not make sense. This
+Exception thrown to signal that the given arguments do not make sense. This
 exception takes a string as an argument.
 
 
@@ -279,7 +264,7 @@ let isValidEmail = email => {
   let hasAtSign = String.includes(email, "@")
   let hasDot = String.includes(email, ".")
   if !(hasAtSign && hasDot) {
-    raise(Failure("Invalid email address"))
+    throw(Failure("Invalid email address"))
   } else {
     true
   }
@@ -295,28 +280,27 @@ let isValid = try isValidEmail("rescript.org") catch {
 ```
 
 ```js
-import * as Caml_js_exceptions from "./stdlib/caml_js_exceptions.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
 function isValidEmail(email) {
-  var hasAtSign = email.includes("@");
-  var hasDot = email.includes(".");
+  let hasAtSign = email.includes("@");
+  let hasDot = email.includes(".");
   if (hasAtSign && hasDot) {
     return true;
   }
   throw {
-        RE_EXN_ID: "Failure",
-        _1: "Invalid email address",
-        Error: new Error()
-      };
+    RE_EXN_ID: "Failure",
+    _1: "Invalid email address",
+    Error: new Error()
+  };
 }
 
-var isValid;
+let isValid;
 
 try {
   isValid = isValidEmail("rescript.org");
-}
-catch (raw_msg){
-  var msg = Caml_js_exceptions.internalToOCamlException(raw_msg);
+} catch (raw_msg) {
+  let msg = Primitive_exceptions.internalToException(raw_msg);
   if (msg.RE_EXN_ID === "Failure") {
     console.error(msg._1);
     isValid = false;
@@ -330,12 +314,12 @@ catch (raw_msg){
 
 ### `Division_by_zero`
 
-Exception raised by integer division and remainder operations when their second argument is zero.
+Exception thrown by integer division and remainder operations when their second argument is zero.
 
 
 <CodeTab labels={["ReScript", "JS Output"]}>
 ```res example
-// ReScript raise `Division_by_zero` if the denominator is zero
+// ReScript throws `Division_by_zero` if the denominator is zero
 let result = try Some(10 / 0) catch {
 | Division_by_zero => None
 }
@@ -344,16 +328,15 @@ Console.log(result) // None
 ```
 
 ```js
-import * as Caml_int32 from "./stdlib/caml_int32.js";
-import * as Caml_js_exceptions from "./stdlib/caml_js_exceptions.js";
+import * as Primitive_int from "./stdlib/Primitive_int.js";
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
-var result;
+let result;
 
 try {
-  result = Caml_int32.div(10, 0);
-}
-catch (raw_exn){
-  var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
+  result = Primitive_int.div(10, 0);
+} catch (raw_exn) {
+  let exn = Primitive_exceptions.internalToException(raw_exn);
   if (exn.RE_EXN_ID === "Division_by_zero") {
     result = undefined;
   } else {
@@ -368,7 +351,7 @@ console.log(result);
 
 ## Catching JS Exceptions
 
-To distinguish between JavaScript exceptions and ReScript exceptions, ReScript namespaces JS exceptions under the `Exn.Error(payload)` variant. To catch an exception thrown from the JS side:
+To distinguish between JavaScript exceptions and ReScript exceptions, ReScript namespaces JS exceptions under the `JsExn(payload)` variant. To catch an exception thrown from the JS side:
 
 
 Throw an exception from JS:
@@ -392,32 +375,35 @@ try {
   // call the external method
   someJSFunctionThatThrows()
 } catch {
-| Exn.Error(obj) =>
-  switch Exn.message(obj) {
+| JsExn(exn) =>
+  switch JsExn.message(exn) {
   | Some(m) => Console.log("Caught a JS exception! Message: " ++ m)
   | None => ()
   }
 }
 ```
 
-The `obj` here is of type `Exn.t`, intentionally opaque to disallow illegal operations. To operate on `obj`, do like the code above by using the standard library's [`Exn`](api/js/exn) module's helpers.
+The payload `exn` here is of type `unknown` since in JS you can throw anything. To operate on `exn`, do like the code above by using the standard library's [`JsExn`](api/core/jsexn) module's helpers
+or use [`Type.Classify.classify`](api/core/type/classify#value-classify) to get more information about the runtime type of `exn`.
 
-## Raise a JS Exception
+## Throw a JS Exception
 
-`raise(MyException)` raises a ReScript exception. To raise a JavaScript exception (whatever your purpose is), use `Exn.raiseError`:
+### Throw a JS Error
+
+`throw(MyException)` throws a ReScript exception. To throw a JavaScript error (whatever your purpose is), use `JsError.throwWithMessage`:
 
 <CodeTab labels={["ReScript", "JS Output"]}>
 
 ```res example
 let myTest = () => {
-  Exn.raiseError("Hello!")
+  JsError.throwWithMessage("Hello!")
 }
 ```
 ```js
-var Js_exn = require("./stdlib/js_exn.js");
+import * as Stdlib_JsError from "./stdlib/Stdlib_JsError.js";
 
 function myTest() {
-  return Js_exn.raiseError("Hello!");
+  return Stdlib_JsError.throwWithMessage("Hello!");
 }
 ```
 
@@ -434,6 +420,36 @@ try {
 }
 ```
 
+### Throw a value that is not an JS Error
+
+If you want to throw any value that is not a valid JS Error, use `JsExn.throw`:
+
+<CodeTab labels={["ReScript", "JS Output"]}>
+
+```res example
+let myTest = () => {
+  JsExn.throw("some non-error value!")
+}
+```
+```js
+function myTest() {
+  throw "some non-error value!";
+}
+```
+
+</CodeTab>
+
+Then you can catch it from the JS side:
+
+```js
+// after importing `myTest`...
+try {
+  myTest()
+} catch (message) {
+  console.log(message) // "Hello!"
+}
+```
+
 ## Catch ReScript Exceptions from JS
 
 The previous section is less useful than you think; to let your JS code work with your exception-throwing ReScript code, the latter doesn't actually need to throw a JS exception. ReScript exceptions can be used by JS code!
@@ -444,13 +460,13 @@ The previous section is less useful than you think; to let your JS code work wit
 exception BadArgument({myMessage: string})
 
 let myTest = () => {
-  raise(BadArgument({myMessage: "Oops!"}))
+  throw(BadArgument({myMessage: "Oops!"}))
 }
 ```
 ```js
-var Caml_exceptions = require("./stdlib/caml_exceptions.js");
+import * as Primitive_exceptions from "./stdlib/Primitive_exceptions.js";
 
-var BadArgument = Caml_exceptions.create("Playground.BadArgument");
+let BadArgument = /* @__PURE__ */Primitive_exceptions.create("Playground.BadArgument");
 
 function myTest() {
   throw {
@@ -491,7 +507,7 @@ try {
 } catch {
 | Not_found => ... // catch a ReScript exception
 | Invalid_argument(_) => ... // catch a second ReScript exception
-| Exn.Error(obj) => ... // catch the JS exception
+| JsExn(exn) => ... // catch the JS exception
 }
 ```
 
diff --git a/pages/docs/manual/v12.0.0/function.mdx b/pages/docs/manual/v12.0.0/function.mdx
index 73a56051e..8138b32a7 100644
--- a/pages/docs/manual/v12.0.0/function.mdx
+++ b/pages/docs/manual/v12.0.0/function.mdx
@@ -446,7 +446,7 @@ Both JS exceptions and exceptions defined in ReScript can be caught. The compile
 ```res example
 exception SomeReScriptException
 
-let somethingThatMightThrow = async () => raise(SomeReScriptException)
+let somethingThatMightThrow = async () => throw(SomeReScriptException)
 
 let someAsyncFn = async () => {
   switch await somethingThatMightThrow() {
diff --git a/pages/docs/manual/v12.0.0/lazy-values.mdx b/pages/docs/manual/v12.0.0/lazy-values.mdx
index 228912bfa..9e541f608 100644
--- a/pages/docs/manual/v12.0.0/lazy-values.mdx
+++ b/pages/docs/manual/v12.0.0/lazy-values.mdx
@@ -62,7 +62,7 @@ The first time `Lazy.get` is called, the expensive computation happens and the r
 
 ## Exception Handling
 
-For completeness' sake, our files read example might raise an exception because of `readdirSync`. Here's how you'd handle it:
+For completeness' sake, our files read example might throw an exception because of `readdirSync`. Here's how you'd handle it:
 
 <CodeTab labels={["ReScript", "JS Output"]}>
 
diff --git a/pages/docs/manual/v12.0.0/overview.mdx b/pages/docs/manual/v12.0.0/overview.mdx
index 7f3049b73..bb2269eab 100644
--- a/pages/docs/manual/v12.0.0/overview.mdx
+++ b/pages/docs/manual/v12.0.0/overview.mdx
@@ -187,8 +187,8 @@ canonical: "/docs/manual/v12.0.0/overview"
 
 | JavaScript                                | ReScript                                     |
 | ----------------------------------------- | -------------------------------------------- |
-| `throw new SomeError(...)`                | `raise(SomeError(...))`                      |
-| `try {a} catch (err) {...} finally {...}` | `try a catch { \| SomeError(err) => ...}` \* |
+| `throw new SomeError(...)`                | `throw(SomeException(...))`                      |
+| `try {a} catch (err) {...} finally {...}` | `try a catch { \| SomeException(err) => ...}` \* |
 
 \* No finally.
 
diff --git a/pages/docs/manual/v12.0.0/scoped-polymorphic-types.mdx b/pages/docs/manual/v12.0.0/scoped-polymorphic-types.mdx
index e0076f9c6..e0c1a3d23 100644
--- a/pages/docs/manual/v12.0.0/scoped-polymorphic-types.mdx
+++ b/pages/docs/manual/v12.0.0/scoped-polymorphic-types.mdx
@@ -92,9 +92,9 @@ Scoped polymorphic types work only when they are directly applied to let-binding
 ```res
 exception Abort
 
-let testExn: 'a. unit => 'a = () => raise(Abort) // Works!
+let testExn: 'a. unit => 'a = () => throw(Abort) // Works!
 
-let testExn2 = (): 'a. 'a = raise(Abort) // Syntax error!
+let testExn2 = (): 'a. 'a = throw(Abort) // Syntax error!
 type fn = 'a. 'a => unit // Syntax error!
 ```