diff --git a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js
index 65552a23f263a..7e1955b753107 100644
--- a/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js
+++ b/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMForm-test.js
@@ -388,4 +388,45 @@ describe('ReactFlightDOMForm', () => {
expect(form.target).toBe('/permalink');
});
+
+ // @gate enableFormActions
+ // @gate enableAsyncActions
+ it('useFormState `permalink` is coerced to string', async () => {
+ const serverAction = serverExports(function action(prevState) {
+ return {state: prevState.count + 1};
+ });
+
+ class Permalink {
+ toString() {
+ return '/permalink';
+ }
+ }
+
+ const permalink = new Permalink();
+
+ const initialState = {count: 1};
+ function Client({action}) {
+ const [state, dispatch] = useFormState(action, initialState, permalink);
+ return (
+
+ );
+ }
+ const ClientRef = await clientExports(Client);
+
+ const rscStream = ReactServerDOMServer.renderToReadableStream(
+ ,
+ webpackMap,
+ );
+ const response = ReactServerDOMClient.createFromReadableStream(rscStream);
+ const ssrStream = await ReactDOMServer.renderToReadableStream(response);
+ await readIntoContainer(ssrStream);
+
+ const form = container.firstChild;
+ const span = container.getElementsByTagName('span')[0];
+ expect(span.textContent).toBe('Count: 1');
+
+ expect(form.target).toBe('/permalink');
+ });
});
diff --git a/packages/react-server/src/ReactFizzHooks.js b/packages/react-server/src/ReactFizzHooks.js
index 9a031a1f3409d..377c21a9fe637 100644
--- a/packages/react-server/src/ReactFizzHooks.js
+++ b/packages/react-server/src/ReactFizzHooks.js
@@ -41,6 +41,7 @@ import {
REACT_CONTEXT_TYPE,
REACT_MEMO_CACHE_SENTINEL,
} from 'shared/ReactSymbols';
+import {checkAttributeStringCoercion} from 'shared/CheckStringCoercion';
type BasicStateAction = (S => S) | S;
type Dispatch = A => void;
@@ -575,8 +576,11 @@ function useFormState(
// $FlowIgnore[prop-missing]
const metadata: ReactCustomFormAction = boundAction.$$FORM_ACTION(prefix);
// Override the target URL
- if (typeof permalink === 'string') {
- metadata.target = permalink;
+ if (permalink !== undefined) {
+ if (__DEV__) {
+ checkAttributeStringCoercion(permalink, 'target');
+ }
+ metadata.target = permalink + '';
}
return metadata;
};