-
Notifications
You must be signed in to change notification settings - Fork 1
Developing Frontends for Extensions
This documentation is no longer up to date and won't be maintained anymore. For an up to date version, please see our Developer Portal.
In mStudio, there are two main ways to implement a frontend for your extensions:
- How it works: Opens in a new tab or window.
- Technology and design: You have complete freedom to choose any technology stack and design your extension however you like.
- When to use it: Ideal if you need full control over your extension’s design and behavior.
External frontends operate independently from mStudio by calling an external service. This approach is perfect if you don't need deep integration with the mStudio environment.
- How it works: Your extension is embedded directly inside mStudio using an iFrame.
- Technology and design: You build using the same UI components as mStudio, creating a seamless user experience.
- When to use it: Best suited when you want tight integration into mStudio while maintaining a consistent look and feel.
Mehr Details zu dieser Variante findest du im Abschnitt Benefits of Embedded Frontends.
- Provides a consistent and user-friendly interface by reusing mStudio’s UI components.
- Leverages predefined "attachment points" (called "Frontend Fragments") to integrate your extension into the mStudio interface.
- Creates a seamless experience for users without switching between different systems.
Follow these steps to build an extension that uses Frontend Fragments:
- Pick a fragment from the list of available integration points, such as the project menu or a customer detail view.
- Set up the selected fragment according to the guide: Storing Frontend Fragments in Your Extension.
- During development, you can point your fragment’s URL to
http://localhost:3000
. This lets you build and test your extension locally before deploying it live. - You can also create a hidden copy of your extension for local development.
- A more streamlined development environment for Frontend Fragments is coming soon to make the process even easier.
- Use
@mittwald/flow-remote-react-components
to develop your frontend. - See the next section for details
Embedded frontends use a remote architecture that mirrors web components rendered inside an iFrame as React components into mStudio. Therefore, development cannot (yet) happen entirely locally — you must use an mStudio instance with an installed extension as the entry point.
Currently, it is recommended to build your frontend with React, because ready-to-use remote components for Flow are available (Flow Components Documentation). You are free to choose any framework you like, such as Vite, Next.js, or others.
Your frontend should be running on http://localhost:3000
, and an appropriate
frontend fragment should be configured
(see Setting up Fragments).
First, install Flow Remote Components:
npm install @mittwald/flow-remote-react-components
Example React component:
import {
Alert,
CodeBlock,
Content,
Heading,
} from "@mittwald/flow-remote-react-components";
import RemoteRoot from "@mittwald/flow-remote-react-components/RemoteRoot";
import { useConfig } from "@mittwald/ext-bridge/react";
function SessionInfo() {
const config = useConfig();
return <InlineCode>{config.sessionId}</InlineCode>;
}
export default function Demo() {
return (
<RemoteRoot>
<Alert>
<Heading>Hello World!</Heading>
<Content>
This is my first extension: <SessionInfo />
</Content>
</Alert>
</RemoteRoot>
);
}
This component is the root of your extension. React components placed under
<RemoteRoot>
are synchronized into the Fragment in mStudio. It should only
appear once in your app and the components you may use are limited. See the
caveats about Limited Components. When the
<RemoteComponent>
is mounted, you have access to the Ext Bridge.
It is important to use the remote version of Flow components. Instead of
@mittwald/flow-react-components
, you must import from
@mittwald/flow-remote-react-components
.
You can find a full documentation of available components here.
Components below the <RemoteRoot>
do have access to the Ext Bridge
configuration by using the useConfig()
hook. The config holds some relevant
information, like the userId
, projectId
or sessionId
.
In order to test if the integration is working correctly, you should first start your dev server. Then install the extension via the mStudio and navigate to the extension page by clicking the menu item of the project or customer main navigation on the left. You should now see the "Hello World" example 🎉
There are some limitations, you should know when developing with remote components.
Under the <RemoteRoot>
component it is only allowed to render the following
components: components except:
- Flow Components
<ul>
<ol>
<li>
<strong>
You can also build up your own components, but finally they must use the components listed above.
If you use other components you will see an error in the console, telling what component is not supported.
The following props will not be applied in the mStudio view. Therefore it is not possible, to ship your own CSS or styles.
className
style
Most of the event data will be accessible by your event handlers, as you would expect when developing with local React. There are some edge cases where the event data may be incomplete. This is because events will be first serialized and then "transported back" to your extension. This is especially limits the use of native browser APIs. If you need support here, please file an issue.
If you need to customize the mStudio page header (title, breadcrumb, actions), you can use the @mittwald/mstudio-ext-react-components
package.
Adding your own backend makes sense if you:
- Want to persist data (e.g., user settings, logs, or other stored information),
- Need to interact with the mittwald API (e.g., fetch project or customer data),
- Require server-side logic or external integrations (e.g., data processing, providing custom APIs).
Having a backend gives you full flexibility to define server-side processes, manage sessions, or build complex business logic.
Weitere Details dazu, wie du eine sichere Kommunikation zwischen Frontend und Backend umsetzt, findest du im Abschnitt Session Handling and Configuration Values via Ext Bridge.
Because embedded frontends run inside an iFrame, browser restrictions can make cookie handling difficult.
To solve this, you can use @mittwald/ext-bridge
to securely share a session
token between mStudio, your frontend, and your backend — keeping the session
intact. Your backend can then verify the session token to authenticate requests.
Additionally, the Ext Bridge allows you to retrieve parameters like userId
or
projectId
. See getConfig()
.
npm add @mittwald/ext-bridge
- Your frontend creates a session token before every backend request. Note: Session tokens expire quickly — generate a fresh token for each request.
- Your backend verifies the token.
- Your backend (optionally) uses information like
userId
for authentication. - (Optional) Your backend creates an access token to interact with the mittwald API.
-
A Session Token is generated by your frontend using
@mittwald/ext-bridge
.
It is short-lived and used to authenticate communication between your frontend and your own backend.
The session token proves that a request originates from a logged-in user inside mStudio. -
An Access Token, on the other hand, is generated server-side by your backend.
It uses the session token and your extension’s secret to create a token that allows access to the mittwald API.
Access tokens are needed when your backend wants to fetch or modify project, customer, or other data directly from mittwald services.
Important: Always create a fresh access token on demand. Never cache or reuse access tokens.
Use getSessionToken()
to generate a session token.
Important:
- Server-Side Rendering (SSR) is not supported — the function must be called client-side.
- The method is available only after the
<RemoteRoot>
component has been rendered. - Therefore, do not call the method during initial rendering (e.g., inside
useEffect
), but only right before a backend request.
Example:
import { getSessionToken } from "@mittwald/ext-bridge/browser";
const token = await getSessionToken();
Since tokens are short-lived, it’s recommended to use an HTTP client middleware or interceptor to attach a fresh token to each request automatically.
Example using Axios:
import { getSessionToken } from "@mittwald/ext-bridge/browser";
axios.interceptors.request.use(async (request) => {
const token = await getSessionToken();
request.headers.set("x-session-token", token);
return request;
});
Through these methods, you can access parameters useful for your frontend implementation. The return type is:
type ExtBridgeConfig = {
sessionId: string;
userId: string;
extensionId: string;
extensionInstanceId: string;
appInstallationId?: string;
projectId?: string;
customerId?: string;
};
If you are using React, you can use the useConfig()
hook.
Use the verify()
method to validate a session token. If successful, it
confirms that the request was made by a logged-in user from mStudio; otherwise,
an error is thrown.
Important: Always create a fresh access token on demand. Never cache or reuse access tokens.
The verify()
method returns a verified and decoded session token with the
following structure:
type SessionTokenPayload = {
sessionId: string;
userId: string;
extensionId: string;
extensionInstanceId: string;
contextId: string;
context: "project" | "organization";
scopes: string[];
};
Example of an Express middleware that verifies the session token and attaches the payload to the request object:
import { verify } from "@mittwald/ext-bridge";
app.use((req, res, next) => {
const token = req.headers["x-session-token"];
if (!token) return next("router");
verify(token)
.then((session) => {
req.sessionToken = session;
next();
})
.catch(() => res.sendStatus(401));
});
You can also use the session token to get an access token to communicate with the mittwald API.
This operation is secured by your global extension secret. You can create one
by using the API route
https://api.mittwald.de/v2/contributors/{contributorId}/extensions/{extensionId}/extension-instances/{extensionInstanceId}/secret
as
documented here.
If you got the extension secret make it securely accessible by your backend.
Note: Do not cache nor store access token – always get a new one.
Now you can use the getAccessToken
method as follows:
import { getAccessToken } from "@mittwald/ext-bridge/node";
const accessToken = await getAccessToken(
sessionTokenFromRequest,
process.env.MY_EXT_SECRET
);
const projectsResponse = await fetch("https://api.mittwald.de/v2/projects", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
Fragment Path | Description | Additional Properties |
---|---|---|
/projects/project/menu/section/extensions/item |
Menu entry in the project overview | projectId |
/projects/project/apps/detail/menu-top/item |
Tab in an app's detail view |
projectId , appId
|
/customers/customer/menu/section/extensions/item |
Menu entry in an organization | customerId |
See the section Building a New Extension with Frontend Fragments for how to use these fragments in your extension.
To register your frontend fragments, call the API:
PATCH https://api.mittwald.de/v2/contributors/{contributorId}/extensions/{extensionId}
{
"/projects/project/menu/section/extensions/item": {
"url": "https://my-extension.com/project/:projectId",
"additionalProperties": {
"icon": "<svg>...</svg>",
"title": "{\"de\": \"Meine Extension\"}"
}
}
}
Under additionalProperties
you can define the following:
- custom icon
-
translated title: Must be an escaped valid JSON string with object
format
{ [languageKey: string]: string }
. The following language keys are supported:de
.