Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

Calling setUndecorated(true) removes the native title bar and border, so the operating system no longer provides its usual window-dragging and edge-resizing controls. To bring those interactions back, add a custom title bar with a mouse-drag handler and a resize grip with its own size-calculation logic. The example below runs on Swing’s Event Dispatch Thread, preserves a minimum size, and can be copied into a Java project.

Set undecorated before showing the frame

Set the frame’s decoration state before it becomes displayable. In practice, do this immediately after constructing the JFrame, before making it visible or creating its native peer. Calling setUndecorated(true) too late can throw IllegalComponentStateException. Oracle’s Frame API documents this lifecycle requirement.

JFrame frame = new JFrame("My window");
frame.setUndecorated(true);
// Add content, set bounds, and then show the frame.

setResizable(true) can still express that the frame is intended to be resizable, but it does not restore a native resize border or implement resizing for an undecorated frame. Your application must calculate and apply the new size itself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Make a custom title bar draggable

When the user presses the title bar, remember where the pointer is within it. While the mouse is dragged, get the pointer’s screen coordinates, subtract that saved offset, and move the frame. Keeping the offset prevents the window from jumping so its top-left corner lands under the cursor.

MouseAdapter dragHandler = new MouseAdapter() {
    private Point pressOffset;

    @Override
    public void mousePressed(MouseEvent e) {
        pressOffset = e.getPoint();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        Point screen = e.getLocationOnScreen();
        frame.setLocation(screen.x - pressOffset.x,
                          screen.y - pressOffset.y);
    }
};

titleBar.addMouseListener(dragHandler);
titleBar.addMouseMotionListener(dragHandler);

Mouse events go to the component under the pointer. If the title bar contains a label or other non-interactive child, attach the same listeners to that child too. Do not attach the drag handler to buttons or other controls that should respond to clicks. Making the entire window draggable is usually a poor choice because it interferes with text selection, scrolling, tables, and other content interactions.

Add a bottom-right resize grip

A resize grip is a simple, visible affordance. Record the pointer’s screen position and the frame’s initial size on press. During dragging, add the pointer movement to that starting size and clamp the result to the minimum dimensions.

private static final int MIN_WIDTH = 360;
private static final int MIN_HEIGHT = 220;

JPanel grip = new JPanel();
grip.setPreferredSize(new Dimension(16, 16));
grip.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));

MouseAdapter resizeHandler = new MouseAdapter() {
    private Point startMouse;
    private Dimension startSize;

    @Override
    public void mousePressed(MouseEvent e) {
        startMouse = e.getLocationOnScreen();
        startSize = frame.getSize();
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        Point now = e.getLocationOnScreen();
        int width = Math.max(MIN_WIDTH,
                startSize.width + now.x - startMouse.x);
        int height = Math.max(MIN_HEIGHT,
                startSize.height + now.y - startMouse.y);
        frame.setSize(width, height);
    }
};

grip.addMouseListener(resizeHandler);
grip.addMouseMotionListener(resizeHandler);

Calling frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT)) is useful, but the custom resize handler should enforce the limits itself before applying each size. A maximum can be handled similarly with Math.min or an explicit bounds check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Complete runnable example

This example supports dragging from its dark title bar and resizing from the lower-right grip. The close button has its own action and is deliberately not part of the draggable area.

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class UndecoratedFrameDemo {
    private static final int MIN_WIDTH = 360;
    private static final int MIN_HEIGHT = 220;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(UndecoratedFrameDemo::createAndShowGui);
    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("Undecorated JFrame");
        // Must be set before the frame becomes displayable.
        frame.setUndecorated(true);
        frame.setResizable(true);
        frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));

        JPanel root = new JPanel(new BorderLayout());
        root.setBorder(BorderFactory.createLineBorder(new Color(80, 80, 80)));
        root.setBackground(new Color(245, 245, 245));

        JPanel titleBar = new JPanel(new BorderLayout());
        titleBar.setPreferredSize(new Dimension(0, 36));
        titleBar.setBackground(new Color(45, 45, 48));

        JLabel title = new JLabel("  Undecorated JFrame");
        title.setForeground(Color.WHITE);

        JButton closeButton = new JButton("×");
        closeButton.setToolTipText("Close");
        closeButton.setFocusPainted(false);
        closeButton.setBorderPainted(false);
        closeButton.setContentAreaFilled(false);
        closeButton.setForeground(Color.WHITE);
        closeButton.setFont(closeButton.getFont().deriveFont(Font.BOLD, 18f));
        closeButton.addActionListener(event -> frame.dispose());

        titleBar.add(title, BorderLayout.CENTER);
        titleBar.add(closeButton, BorderLayout.EAST);

        MouseAdapter dragHandler = new MouseAdapter() {
            private Point pressOffset;

            @Override
            public void mousePressed(MouseEvent e) {
                pressOffset = e.getPoint();
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                Point screen = e.getLocationOnScreen();
                frame.setLocation(screen.x - pressOffset.x,
                                  screen.y - pressOffset.y);
            }
        };
        titleBar.addMouseListener(dragHandler);
        titleBar.addMouseMotionListener(dragHandler);
        title.addMouseListener(dragHandler);
        title.addMouseMotionListener(dragHandler);

        JPanel content = new JPanel(new GridBagLayout());
        content.add(new JLabel("Drag the title bar to move; drag the lower-right grip to resize."));

        JPanel grip = new JPanel();
        grip.setPreferredSize(new Dimension(16, 16));
        grip.setBackground(new Color(180, 180, 180));
        grip.setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));

        MouseAdapter resizeHandler = new MouseAdapter() {
            private Point startMouse;
            private Dimension startSize;

            @Override
            public void mousePressed(MouseEvent e) {
                startMouse = e.getLocationOnScreen();
                startSize = frame.getSize();
            }

            @Override
            public void mouseDragged(MouseEvent e) {
                Point now = e.getLocationOnScreen();
                int width = Math.max(MIN_WIDTH,
                        startSize.width + now.x - startMouse.x);
                int height = Math.max(MIN_HEIGHT,
                        startSize.height + now.y - startMouse.y);
                frame.setSize(width, height);
            }
        };
        grip.addMouseListener(resizeHandler);
        grip.addMouseMotionListener(resizeHandler);

        root.add(titleBar, BorderLayout.NORTH);
        root.add(content, BorderLayout.CENTER);
        root.add(grip, BorderLayout.SOUTH);
        frame.setContentPane(root);
        frame.setSize(700, 450);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

All UI creation and updates in the example occur on Swing’s Event Dispatch Thread because startup is wrapped in SwingUtilities.invokeLater. Keep drag handlers lightweight; doing file or database work during mouseDragged can make movement feel unresponsive.

What this example does—and does not—resize

The grip changes width and height from the bottom-right corner only. Native borders normally support hit-testing on several edges and corners, so this is not equivalent to full native resizing. To support all sides, define eight hit zones: north, south, east, west, and the four corners. For each zone, calculate the corresponding changes to the frame’s position and dimensions. For example, west-edge resizing changes both x and width:

int deltaX = currentMouseScreen.x - initialMouseScreen.x;
int newX = initialBounds.x + deltaX;
int newWidth = initialBounds.width - deltaX;

if (newWidth >= minimumWidth) {
    frame.setBounds(newX, initialBounds.y, newWidth, initialBounds.height);
}

For north and west edges, clamp the size while adjusting the position so the opposite edge stays in place. Otherwise, the frame can jump when the minimum size is reached.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Common problems

  • IllegalComponentStateException when setting undecorated: Set setUndecorated(true) before displayability, not merely while the frame is hidden. If the frame already has a native peer, create a new frame with the desired decoration state.
  • Dragging does not work over the title text: The label is a separate component, so attach the drag listeners to it as well as the title-bar panel. Check that both mouse and mouse-motion listeners are registered.
  • The window jumps when dragging starts: Subtract the press offset from the current screen position instead of setting the frame origin to the pointer.
  • The close button moves the frame: Keep interactive controls outside the draggable listener attachments.
  • The grip does not receive events: Check that it is not covered by another component and that the layout has not reduced its displayed size.
  • The frame can shrink too far: Clamp width and height in the resize calculation before calling setSize or setBounds.

When to choose a different approach

If you do not need a fully custom window shape or title bar, keeping native decorations is usually simpler: the operating system retains dragging, resizing, accessibility support, and its usual window controls. A middle ground is Java look-and-feel-provided window decoration. Oracle documents JFrame.setDefaultLookAndFeelDecorated and JRootPane.setWindowDecorationStyle; for example, a frame can be undecorated at the native level and request a look-and-feel frame style with frame.getRootPane().setWindowDecorationStyle(JRootPane.FRAME). Appearance and support depend on the active look and feel and environment, so this is not the same as native chrome or a hand-built title bar. See the JFrame API.

A custom frame also needs any controls you want beyond moving and resizing—such as minimize, maximize, restore, and keyboard-accessible close behavior. If implementing maximize, save the normal bounds and use the active screen’s usable bounds rather than assuming the display begins at (0, 0) or that the entire display is unobstructed. Test on the operating systems and monitor setups you support, including high-DPI scaling, monitors positioned left or above the primary display, and transitions between monitors. Screen-coordinate mouse handling is a practical foundation, but these details and shaped-window behavior can vary by environment.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.